Hướng dẫn developing restful web apis with python flask and mongodb - phát triển apis web yên tĩnh với python flask và mongodb

Đây là phần đầu tiên của một loạt các bài đăng trên blog có tên là "Viết lại nó trong Rust (RIIR)." Đó là một tiêu đề tặc lưỡi cho một số bài đăng sẽ điều tra những điểm tương đồng và khác biệt giữa cùng một dịch vụ được viết bằng Python với Flask và Rust với Actix-Web.

Bài đăng này sẽ cho thấy cách tôi xây dựng một API RESTful cho một bộ sưu tập các công thức nấu ăn cocktail mà tôi tình cờ nói dối. Mục đích là để hiển thị một máy chủ API với một số phức tạp, vì vậy mặc dù đó là một ví dụ nhỏ, nó sẽ bao gồm các yếu tố quan trọng như:

  • Chuyển đổi dữ liệu giữa cơ sở dữ liệu và biểu diễn JSON.

Điều kiện tiên quyết

  • Một cụm Atlas MongoDB. Thực hiện theo hướng dẫn "Bắt đầu với Atlas" để tạo tài khoản và cụm mongoDB của bạn. Giữ một lưu ý về tên người dùng, mật khẩu và chuỗi kết nối cơ sở dữ liệu của bạn vì bạn sẽ cần những thứ này sau.Get Started with Atlas" guide to create your account and MongoDB cluster. Keep a note of your database username, password, and connection string as you will need those later.

Đây là một hướng dẫn nâng cao, vì vậy nó sẽ bao gồm cả một loạt các thư viện khác nhau có thể được kết hợp với nhau để xây dựng một máy chủ API RESTful khai báo trên đỉnh MongoDB. Tôi sẽ không bao gồm các mẫu lặp lại trong cơ sở mã, vì vậy nếu bạn muốn xây dựng toàn bộ sự việc, tôi khuyên bạn nên kiểm tra mã nguồn, tất cả đều có trên GitHub.advanced guide, so it'll cover a whole bunch of different libraries which can be brought together to build a declarative Restful API server on top of MongoDB. I won't cover repeating patterns in the codebase, so if you want to build the whole thing, I recommend checking out the source code, which is all on GitHub.

Nó sẽ không bao gồm những điều cơ bản của Python, Flask hoặc MongoDB, vì vậy nếu đó là những gì bạn đang tìm kiếm, tôi khuyên bạn nên kiểm tra các tài nguyên sau khi giải quyết bài đăng này:

Bắt đầu

Bắt đầu bằng cách nhân bản nguồn mã mẫu từ GitHub. Có bốn thư mục cấp cao nhất:from GitHub. There are four top-level directories:

  • Dữ liệu: Điều này chứa xuất khẩu dữ liệu cocktail của tôi. Bạn sẽ nhập nó vào cụm của bạn trong giây lát.: This contains an export of my cocktail data. You'll import this into your cluster in a moment.

  • test_scripts: Một vài tập lệnh shell sử dụng Curl để kiểm tra giao diện HTTP của máy chủ API.: A few shell scripts that use curl to test the HTTP interface of the API server.

Có nhiều chi tiết hơn trong repo GitHub, nhưng những điều cơ bản là: Cài đặt dự án với VirtualEnv Active:GitHub repo, but the basics are: Install the project with your virtualenv active:

Hướng dẫn developing restful web apis with python flask and mongodb - phát triển apis web yên tĩnh với python flask và mongodb

Tiếp theo, bạn nên nhập dữ liệu vào cụm của bạn. Đặt biến môi trường $MONGO_URI thành URI cụm của bạn. Biến môi trường này sẽ được sử dụng trong một thời điểm để nhập dữ liệu của bạn và cả ứng dụng Flask. Tôi sử dụng direnv để định cấu hình này và đặt dòng sau vào tệp .envrc của tôi vào thư mục của dự án:$MONGO_URI to your cluster URI. This environment variable will be used in a moment to import your data, and also by the Flask app. I use direnv to configure this, and put the following line in my .envrc file in my project's directory:

Lưu ý rằng cơ sở dữ liệu của bạn phải được gọi là "cocktail" và việc nhập sẽ tạo ra một bộ sưu tập có tên là "Công thức nấu ăn". Sau khi kiểm tra xem $MONGO_URI có được đặt chính xác không, hãy chạy lệnh sau:$MONGO_URI is set correctly, run the following command:

Bây giờ bạn sẽ có thể chạy ứng dụng bình từ thư mục flask-cocktail-api:flask-cocktail-api directory:

(Bạn có thể chạy make run nếu bạn thích.)make run if you prefer.)

Kiểm tra đầu ra để đảm bảo nó hài lòng với cấu hình và sau đó trong một cửa sổ đầu cuối khác, chạy tập lệnh list_cocktails.sh trong thư mục test_scripts. Nó nên in một cái gì đó như thế này:list_cocktails.sh script in the test_scripts directory. It should print something like this:

Phá vỡ tất cả

Mã được chia thành ba mô hình con.

  • __init__.py chứa tất cả mã thiết lập bình và xác định tất cả các tuyến HTTP. contains all the Flask setup code, and defines all the HTTP routes.

  • model.py chứa tất cả các định nghĩa mô hình Pydantic. contains all the Pydantic model definitions.

  • direnv0 chứa một định nghĩa trường pydantic mà tôi đã đánh cắp từ bản đồ dữ liệu đối tượng beanie cho MongoDB. contains a Pydantic field definition that I stole from the Beanie object-data mapper for MongoDB.

Tôi đã đề cập trước đó rằng mã này sử dụng một số thư viện:

  • Pymongo và Flask-Pymongo xử lý kết nối với cơ sở dữ liệu. Flask-Pymongo đặc biệt kết thúc đối tượng thu thập cơ sở dữ liệu để cung cấp phương thức direnv1 thuận tiện. and Flask-PyMongo handle the connection to the database. Flask-PyMongo specifically wraps the database collection object to provide a convenientdirenv1 method.

  • Pydantic quản lý xác thực dữ liệu và một số khía cạnh của chuyển đổi dữ liệu giữa cơ sở dữ liệu và các biểu diễn JSON. manages data validation, and some aspects of data transformation between the database and a JSON representations.

  • cùng với một chức năng duy nhất từ ​​fastapi.FastAPI.

Xác thực và chuyển đổi dữ liệu

Khi xây dựng API mạnh mẽ, điều quan trọng là phải xác nhận tất cả dữ liệu được truyền vào hệ thống. Có thể thực hiện việc này bằng cách sử dụng một ngăn xếp các câu lệnh direnv2, nhưng sẽ hiệu quả hơn nhiều khi xác định một lược đồ khai báo và cho phép điều đó xác thực dữ liệu được đầu vào.direnv2 statements, but it's much more effective to define a schema declaratively, and to allow that to programmatically validate the data being input.

Tôi đã sử dụng một kỹ thuật mà tôi đã học được từ Beanie, một ODM mới và gọn gàng mà tôi không may thực tế không thể sử dụng cho dự án này, bởi vì beanie là async, và Flask là một khung chặn.Beanie, a new and neat ODM that I unfortunately couldn't practically use on this project, because Beanie is async, and Flask is a blocking framework.

Beanie sử dụng Pydantic để xác định lược đồ và thêm loại trường tùy chỉnh cho ObjectID.Pydantic to define a schema, and adds a custom Field type for ObjectId.

Lược đồ direnv3 này xác định cấu trúc của một thể hiện direnv3, sẽ được xác thực bởi Pydantic khi các trường hợp được tạo. Nó bao gồm một lược đồ nhúng khác cho direnv5, được định nghĩa theo cách tương tự.direnv3 schema defines the structure of a direnv3 instance, which will be validated by Pydantic when instances are created. It includes another embedded schema for direnv5, which is defined in a similar way.

Tôi đã thêm các chức năng tiện lợi để xuất dữ liệu trong trường hợp direnv3 vào tương thích với JSON direnv7 hoặc tương thích BSON direnv7. Sự khác biệt là tinh tế, nhưng BSON hỗ trợ các loại direnv9 và .envrc0 bản địa, ví dụ, trong khi khi mã hóa dưới dạng JSON, cần phải mã hóa các phiên bản ObjectID theo một cách khác (tôi thích một chuỗi chứa giá trị hex của ID) và các đối tượng DateTime được mã hóa dưới dạng chuỗi ISO8601.direnv3 instance to either a JSON-compatible direnv7 or a BSON-compatible direnv7. The differences are subtle, but BSON supports native direnv9 and .envrc0 types, for example, whereas when encoding as JSON, it's necessary to encode ObjectId instances in some other way (I prefer a string containing the hex value of the id), and datetime objects are encoded as ISO8601 strings.

Phương pháp .envrc1 sử dụng một hàm được nhập từ Fastapi, tái phát qua dữ liệu thể hiện, mã hóa tất cả các giá trị ở dạng tương thích JSON. Nó đã xử lý chính xác các trường hợp ____2020, nhưng để có được nó để xử lý các giá trị ObjectID, tôi đã trích xuất một số mã trường tùy chỉnh từ beanie, có thể tìm thấy trong ____10..envrc1 method makes use of a function imported from FastAPI, which recurses through the instance data, encoding all values in a JSON-compatible form. It already handles .envrc0 instances correctly, but to get it to handle ObjectId values, I extracted some custom field code from Beanie, which can be found in direnv0.

Phương pháp .envrc4 không cần truyền dữ liệu direnv7 qua .envrc6. Tất cả các loại được sử dụng trong lược đồ có thể được lưu trực tiếp với pymongo. Điều quan trọng là đặt .envrc7 thành .envrc8, do đó, chìa khóa cho .envrc9 chỉ là, .envrc9, chứ không phải $MONGO_URI1 của lược đồ mà không có dấu gạch dưới..envrc4 method doesn't need to pass the direnv7 data through .envrc6. All the types used in the schema can be directly saved with PyMongo. It's important to set .envrc7 to .envrc8, so that the key for .envrc9 is just that, .envrc9, and not the schema's $MONGO_URI1 without an underscore.

Cách tiếp cận này gọn gàng cho trường hợp sử dụng cụ thể này, nhưng tôi không thể cảm thấy rằng nó sẽ bị hạn chế trong một hệ thống phức tạp hơn. Có nhiều mẫu để lưu trữ dữ liệu trong MongoDB. Chúng thường dẫn đến việc lưu trữ dữ liệu ở dạng tối ưu cho việc ghi hoặc đọc, nhưng không nhất thiết là đại diện bạn muốn xuất trong API.patterns for storing data in MongoDB. These often result in storing data in a form that is optimal for writes or reads, but not necessarily the representation you would wish to export in an API.

Nhìn vào lược đồ ở trên, bạn có thể đã tự hỏi "sên" là gì ... tốt, ngoài một loài gây hại trong vườn nhếch nhác.

Một con sên là một url, an toàn, an toàn, được sử dụng để xác định một tài liệu. Tôi đã chọn thuật ngữ như một nhà phát triển Django, nơi thuật ngữ này là một phần của khuôn khổ. Một con sên thường có nguồn gốc từ một lĩnh vực khác. Trong trường hợp này, sên có nguồn gốc từ tên của loại cocktail, vì vậy nếu một ly cocktail được gọi là "rượu whisky lúa mạch đen kiểu cũ", sên sẽ là "Rye-whiskey-old-foothed".

Trong API này, loại cocktail đó có thể được truy cập bằng cách gửi yêu cầu $MONGO_URI2 đến điểm cuối $MONGO_URI3.$MONGO_URI2 request to the $MONGO_URI3 endpoint.

Tôi đã giữ trường $MONGO_URI4 duy nhất tách biệt với trường .envrc9 tự động được chỉ định, nhưng tôi đã cung cấp cả hai vì sên có thể thay đổi nếu tên của cocktail được điều chỉnh lên một tài liệu chính xác.$MONGO_URI4 field separate from the auto-assigned .envrc9 field, but I've provided both because the slug could change if the name of the cocktail was tweaked, in which case the .envrc9 value would provide a constant identifier to look up an exact document.

Trong phiên bản rỉ sét của mã này, tôi đã khỏa thân sử dụng một cách tiếp cận khác. Đó là một chút dài dòng, nhưng cuối cùng tôi đã bị thuyết phục rằng nó sẽ mạnh mẽ và linh hoạt hơn khi hệ thống phát triển.

Tạo một tài liệu mới

Bây giờ tôi sẽ cho bạn thấy một điểm cuối duy nhất trông như thế nào, trước tiên tập trung vào điểm cuối "Tạo", xử lý yêu cầu POST đến $MONGO_URI7 và tạo một tài liệu mới trong bộ sưu tập "công thức nấu ăn". Sau đó, nó trả lại tài liệu được lưu trữ, bao gồm ID mới độc đáo mà MongoDB được gán là .envrc9, bởi vì đây là API RESTful, và đó là những gì API RESTful làm.$MONGO_URI7 and creates a new document in the "recipes" collection. It then returns the document that was stored, including the newly unique ID that MongoDB assigned as .envrc9, because this is a RESTful API, and that's what RESTful APIs do.

Điểm cuối này sửa đổi trực tiếp JSON đến, để thêm một mục $MONGO_URI9 với thời gian hiện tại. Sau đó, nó chuyển nó cho hàm tạo cho lược đồ Pydantic của chúng tôi. Tại thời điểm này, nếu lược đồ không xác thực dữ liệu, một ngoại lệ sẽ được nêu và hiển thị cho người dùng.$MONGO_URI9 item with the current time. It then passes it to the constructor for our Pydantic schema. At this point, if the schema failed to validate the data, an exception would be raised and displayed to the user.

Sau khi xác thực dữ liệu, flask-cocktail-api0 được gọi trên direnv3 để chuyển đổi nó thành Dict tương thích BSON và điều này được chuyển trực tiếp sang phương thức flask-cocktail-api2 của Pymongo. Không có cách nào để Pymongo trả lại tài liệu vừa được chèn vào một thao tác duy nhất (mặc dù UPSERT sử dụng flask-cocktail-api3 tương tự như vậy).flask-cocktail-api0 is called on the direnv3 to convert it to a BSON-compatible dict, and this is directly passed to PyMongo's flask-cocktail-api2 method. There's no way to get PyMongo to return the document that was just inserted in a single operation (although an upsert using flask-cocktail-api3 is similar to just that).

Sau khi chèn dữ liệu, mã sau đó cập nhật đối tượng cục bộ với $MONGO_URI1 mới được gán và trả lại cho máy khách.$MONGO_URI1 and returns it to the client.

Đọc một loại cocktail duy nhất

Nhờ flask-cocktail-api5, điểm cuối đã tìm kiếm một loại cocktail duy nhất thậm chí còn đơn giản hơn:flask-cocktail-api5, the endpoint for looking up a single cocktail is even more straightforward:

Điểm cuối này sẽ hủy bỏ với 404 nếu con sên không thể tìm thấy trong bộ sưu tập. Mặt khác, nó chỉ đơn giản là khởi tạo một ly cocktail với tài liệu từ cơ sở dữ liệu và gọi .envrc1 để chuyển đổi nó thành một câu chuyện rằng bình sẽ tự động mã hóa chính xác dưới dạng JSON..envrc1 to convert it to a dict that Flask will automatically encode correctly as JSON.

Liệt kê tất cả các loại cocktail

Điểm cuối này là một con quái vật, và đó là do phân trang và các liên kết để phân trang. Trong dữ liệu mẫu ở trên, có lẽ bạn đã nhận thấy phần flask-cocktail-api7:flask-cocktail-api7 section:

Phần flask-cocktail-api7 này được chỉ định là một phần của đặc tả HAL (Ngôn ngữ ứng dụng siêu văn bản). Đó là một ý tưởng tốt để làm theo một tiêu chuẩn cho dữ liệu phân trang và tôi không cảm thấy muốn phát minh ra thứ gì đó!flask-cocktail-api7 section is specified as part of the HAL (Hypertext Application Language) specification. It's a good idea to follow a standard for pagination data, and I didn't feel like inventing something myself!

Và đây là mã để tạo tất cả điều này. Đừng hoảng sợ.

Mặc dù có rất nhiều mã ở đó, nhưng nó không phức tạp như lần đầu tiên xuất hiện. Hai yêu cầu được thực hiện cho MongoDB: một cho một công thức nấu ăn cocktail, và một cho tổng số ly cocktail trong bộ sưu tập. Các tính toán khác nhau được thực hiện để tìm ra có bao nhiêu tài liệu để bỏ qua và có bao nhiêu trang cocktail. Cuối cùng, một số liên kết được thêm vào cho các trang "trước" và "tiếp theo", nếu thích hợp (tức là: trang hiện tại không phải là trang đầu tiên hoặc cuối cùng.) Trong một vòng lặp lần này.

Bản cập nhật và xóa các điểm cuối chủ yếu là sự lặp lại của mã tôi đã đưa vào, vì vậy tôi sẽ không đưa chúng vào đây. Kiểm tra chúng trong repo GitHub nếu bạn muốn xem cách chúng hoạt động.GitHub repo if you want to see how they work.

Xử lý lỗi

Không có gì gây khó chịu cho tôi hơn là sử dụng API JSON trả về HTML khi xảy ra lỗi, vì vậy tôi rất muốn đưa ra một số xử lý lỗi hợp lý để tránh điều này xảy ra.

Sau khi mã thiết lập bình và trước các định nghĩa điểm cuối, mã đăng ký hai trình điều khiển lỗi:

Trình điều khiển lỗi đầu tiên chặn bất kỳ điểm cuối nào không thành công với mã trạng thái 404 và đảm bảo rằng lỗi được trả về dưới dạng json dict.

Trình điều khiển lỗi thứ hai chặn một flask-cocktail-api9 được nâng lên bởi bất kỳ điểm cuối nào và thực hiện điều tương tự như trình xử lý lỗi đầu tiên, nhưng đặt mã trạng thái HTTP thành "400 yêu cầu xấu."flask-cocktail-api9 raised by any endpoint, and does the same thing as the first error-handler, but sets the HTTP status code to "400 Bad Request."

Khi tôi viết bài này, tôi nhận ra rằng tôi đã bỏ lỡ một tay cầm lỗi để đối phó với dữ liệu cocktail không hợp lệ. Tôi sẽ để thực hiện nó như một bài tập cho người đọc! Thật vậy, đây là một trong những khó khăn với việc viết các ứng dụng Python mạnh mẽ: bởi vì các trường hợp ngoại lệ có thể được nêu ra từ sâu trong đống phụ thuộc của bạn, rất khó để dự đoán toàn diện những ngoại lệ mà ứng dụng của bạn có thể nêu ra trong các trường hợp khác nhau.

Đây là một cái gì đó rất khác biệt trong rỉ sét, và mặc dù, như bạn sẽ thấy, việc xử lý lỗi trong rỉ sét có thể là dài dòng và khó khăn, tôi đã bắt đầu yêu ngôn ngữ vì sự khăng khăng của nó về sự đúng đắn.

Gói lên

Khi tôi bắt đầu viết bài này, mặc dù tôi sẽ tương đối đơn giản. Khi tôi thêm yêu cầu rằng mã không chỉ là một ví dụ về đồ chơi, một số khó khăn vốn có với việc xây dựng API mạnh mẽ trên đỉnh của bất kỳ cơ sở dữ liệu nào trở nên rõ ràng.

Trong trường hợp này, Flask có thể không phải là công cụ phù hợp cho công việc. Gần đây tôi đã viết một bài đăng trên blog về việc xây dựng một API với beanie. Beanie và Fastapi là một trận đấu được thực hiện trên thiên đường cho loại ứng dụng này và sẽ xử lý xác nhận, chuyển đổi và phân trang với ít mã hơn. Trên hết, họ tự ghi chép lại và có thể cung cấp lược đồ của dữ liệu ở các định dạng mở, bao gồm Specapi Spec và Lược đồ JSON!building an API with Beanie. Beanie and FastAPI are a match made in heaven for this kind of application and will handle validation, transformation, and pagination with much less code. On top of that, they're self-documenting and can provide the data's schema in open formats, including OpenAPI Spec and JSON Schema!

Tôi sẽ sớm xuất bản bài viết thứ hai trong loạt bài này, xây dựng API cocktail với Actix-Web, MongoDB và Rust, và sau đó tôi sẽ kết luận với một bài viết thứ ba, tôi viết lại nó trong Rust. 'LL đánh giá điểm mạnh và điểm yếu của hai thí nghiệm.Build a Cocktail API with Actix-Web, MongoDB, and Rust, and then I'll conclude with a third post, I Rewrote it in Rust—How Did it Go?, where I'll evaluate the strengths and weaknesses of the two experiments.

Cảm ơn bạn đã đọc. Hãy chú ý đến các bài viết sắp tới!

Nếu bạn có thắc mắc, vui lòng truy cập trang web cộng đồng nhà phát triển của chúng tôi nơi các kỹ sư MongoDB và cộng đồng MongoDB sẽ giúp bạn xây dựng ý tưởng lớn tiếp theo của mình với MongoDB.developer community website where the MongoDB engineers and the MongoDB community will help you build your next big idea with MongoDB.

Làm cách nào để tạo API REST với Python và bình?

CONTENTS..
Cài đặt bình ..
Tạo điểm cuối danh sách trong bình ..
Tạo điểm cuối chi tiết trong bình ..
Thêm bộ lọc vào điểm cuối danh sách ..
Xây dựng một điểm cuối tạo ..
Tạo điểm cuối cập nhật ..
Tạo điểm cuối ghi Xóa ..

MongoDB có hỗ trợ API REST không?

API dữ liệu ATLAS là API giống như REST được quản lý đầy đủ, để cho phép bạn truy cập dữ liệu Atlas MongoDB của mình và thực hiện các hoạt động và tập hợp CRUD một cách dễ dàng.Sau khi được bật trên một cụm, bạn có thể đạt được tất cả các hoạt động CRUD ra khỏi hộp thông qua URL, chỉ với khóa API., to allow you to access your MongoDB Atlas data, and perform CRUD operations and aggregations with ease. Once enabled on a cluster, you can achieve all the CRUD operations out of the box via a URL, with just an API key.

Làm cách nào để tạo API REST trong MongoDB?

Bước 1: Thiết lập dự án ..
Bước 2: Cài đặt phụ thuộc ứng dụng ..
Bước 3: Chạy mã ..
Bước 4: Ứng dụng kiểm tra API MongoDB REST ..
Bước 5: Thiết lập kết nối với API MongoDB REST ..
Bước 6: Xây dựng điểm cuối API của MongoDB REST ..
Bước 7: Kiểm tra API REST MongoDB ..

Bình có tốt cho API REST không?

Flask-Restful là một phần mở rộng cho Flask cung cấp hỗ trợ bổ sung cho các API REST tòa nhà.Bạn sẽ không bao giờ thất vọng với thời gian cần thiết để phát triển API.Flask-Restful là một sự trừu tượng nhẹ hoạt động với các thư viện/thư viện hiện có.Flask-Restful khuyến khích thực tiễn tốt nhất với thiết lập tối thiểu.. You will never be disappointed with the time it takes to develop an API. Flask-Restful is a lightweight abstraction that works with the existing ORM/libraries. Flask-RESTful encourages best practices with minimal setup.