Hướng dẫn dùng flask mongodb python

  • Giới thiệu
  • Flask-MongoEngine
    • Thiết lập và cấu hình
    • Kết nối với Phiên bản cơ sở dữ liệu MongoDB
  • Tạo lớp mô hình
    • Tài liệu
    • EmbeddedDocument
    • DynamicDocument
    • DynamicEmbeddedDocument
  • Truy cập tài liệu
    • Nhận một tài liệu
  • Tạo / Lưu tài liệu
    • Tạo tài liệu với Tài liệu nhúng
    • Tạo tài liệu động
  • Cập nhật tài liệu
  • Xóa tài liệu
  • Làm việc với tệp
    • Tạo và lưu trữ tệp
    • Truy xuất tệp
    • Xóa tệp
  • Sự kết luận

Giới thiệu

Xây dựng một ứng dụng web hầu như luôn có nghĩa là xử lý dữ liệu từ cơ sở dữ liệu. Có nhiều cơ sở dữ liệu khác nhau để lựa chọn, tùy thuộc vào sở thích của bạn.

Trong hướng dẫn này, chúng ta sẽ xem xét cách tích hợp một trong những cơ sở dữ liệu NoSQL phổ biến nhất – MongoDB – với khuôn khổ vi mô Flask.

Trong hướng dẫn này, chúng ta sẽ khám phá cách tích hợp MongoDB với Flask bằng cách sử dụng một thư viện phổ biến – MongoEnginevà cụ thể hơn, trình bao bọc của nó – Flask-MongoEngine.

Ngoài ra, bạn có thể tích hợp MongoDB với Flask-PyMongo.

Flask-MongoEngine

MongoEngine là một ODM [Object Document Mapper] ánh xạ các lớp Python [mô hình] sang các tài liệu MongoDB, giúp dễ dàng tạo và thao tác các tài liệu theo chương trình ngay từ mã của chúng tôi.

Thiết lập và cấu hình

Để khám phá một số tính năng của MongoEngine, chúng tôi sẽ tạo một API phim đơn giản cho phép chúng tôi thực hiện các hoạt động CRUD trên Movie các trường hợp.

Để bắt đầu, hãy cài đặt Flask nếu bạn chưa có:

$ pip install flask

Tiếp theo, chúng ta sẽ cần quyền truy cập vào một phiên bản MongoDB, MongoDB cung cấp một phiên bản đám mây – MongoDB Atlas – mà chúng tôi có thể sử dụng miễn phí, tuy nhiên, chúng tôi sẽ sử dụng phiên bản được cài đặt cục bộ. Bạn có thể tìm thấy hướng dẫn để tải và cài đặt MongoDB trong tài liệu chính thức.

Và sau khi hoàn thành, chúng tôi cũng sẽ muốn cài đặt thư viện Flask-MongoEngine:

$ pip install flask-mongoengine

Kết nối với Phiên bản cơ sở dữ liệu MongoDB

Bây giờ chúng ta đã cài đặt Flask và Flask-MongoEngine, chúng ta cần kết nối ứng dụng Flask của mình với một phiên bản MongoDB.

Chúng tôi sẽ bắt đầu bằng cách nhập Flask và Flask-MongoEngine vào ứng dụng của chúng tôi:

from flask import Flask from flask_mongoengine import MongoEngine

Sau đó, chúng ta có thể tạo đối tượng ứng dụng Flask:

app = Flask[__name__]

Cái mà chúng tôi sẽ sử dụng để khởi tạo một MongoEngine. Nhưng trước khi quá trình khởi tạo hoàn tất, chúng tôi sẽ cần tham chiếu đến phiên bản MongoDB của chúng tôi.

Tham chiếu này là một chìa khóa trong app.config có giá trị là một dict chứa các tham số kết nối:

app.config['MONGODB_SETTINGS'] = { 'db':'db_name', 'host':'localhost', 'port':'27017' }

Thay vào đó, chúng tôi cũng có thể cung cấp một URI kết nối:

app.config['MONGODB_SETTINGS'] = { 'host':'mongodb://localhost/db_name' }

Với cấu hình được thực hiện, bây giờ chúng ta có thể khởi tạo một MongoEngine:

db = MongoEngine[app]

Chúng tôi cũng có thể sử dụng init_app[] phương pháp của MongoEngine để khởi tạo:

db = MongoEngine[] db.init_app[app]

Sau khi cấu hình và khởi tạo xong, chúng ta có thể bắt đầu khám phá một số tính năng tuyệt vời của MongoEngine.

Tạo lớp mô hình

Là một ODM, MongoEngine sử dụng các lớp Python để đại diện cho các tài liệu trong cơ sở dữ liệu của chúng tôi.

MongoEngine cung cấp một số loại lớp tài liệu:

  1. Tài liệu
  2. EmbeddedDocument
  3. DynamicDocument
  4. DynamicEmbeddedDocument

Tài liệu

Điều này đại diện cho một tài liệu có bộ sưu tập riêng của nó trong cơ sở dữ liệu, nó được tạo bằng cách kế thừa từ mongoengine.Document hoặc từ của chúng tôi MongoEngine ví dụ [db.Document]:

class Movie[db.Document]: title = db.StringField[required=True] year = db.IntField[] rated = db.StringField[] director = db.ReferenceField[Director] cast = db.EmbeddedDocumentListField[Cast] poster = db.FileField[] imdb = db.EmbeddedDocumentField[Imdb]

MongoEngine cũng cung cấp các lớp bổ sung mô tả và xác thực loại dữ liệu mà các trường của tài liệu sẽ nhận và các công cụ sửa đổi tùy chọn để thêm các chi tiết hoặc ràng buộc khác cho từng trường.

Ví dụ về các trường là:

  1. StringField[] cho các giá trị chuỗi
  2. IntField[] cho các giá trị int
  3. ListField[] cho một danh sách
  4. FloatField[] cho các giá trị dấu phẩy động
  5. ReferenceField[] để tham khảo các tài liệu khác
  6. EmbeddedDocumentField[] cho các tài liệu nhúng, v.v.
  7. FileField[] để lưu trữ các tệp [thêm về điều này sau]

Bạn cũng có thể áp dụng các công cụ sửa đổi trong các trường này, chẳng hạn như:

  • required
  • default
  • unique
  • primary_key vân vân.

Bằng cách đặt bất kỳ trong số này thành Truechúng sẽ được áp dụng cụ thể cho lĩnh vực đó.

EmbeddedDocument

Điều này đại diện cho một tài liệu không có bộ sưu tập riêng của nó trong cơ sở dữ liệu nhưng được nhúng vào một tài liệu khác, nó được tạo bằng cách kế thừa từ EmbeddedDocument lớp:

class Imdb[db.EmbeddedDocument]: imdb_id = db.StringField[] rating = db.DecimalField[] votes = db.IntField[]

DynamicDocument

Đây là tài liệu có các trường được thêm động, tận dụng tính chất động của MongoDB.

Giống như các loại tài liệu khác, MongoEngine cung cấp một lớp học cho DynamicDocumentS:

class Director[db.DynamicDocument]: pass

DynamicEmbeddedDocument

Điều này có tất cả các thuộc tính của DynamicDocumentEmbeddedDocument

class Cast[db.DynamicEmbeddedDocument]: pass

Khi chúng ta hoàn tất việc tạo tất cả các lớp dữ liệu của mình, đã đến lúc bắt đầu khám phá một số tính năng của MongoEngine

Truy cập tài liệu

MongoEngine làm cho việc truy vấn cơ sở dữ liệu của chúng tôi rất dễ dàng, chúng tôi có thể lấy tất cả các bộ phim trong cơ sở dữ liệu như vậy;

from flask import jsonify @app.route['/movies'] def get_movies[]: movies = Movie.objects[] return jsonify[movies], 200

Nếu chúng tôi gửi yêu cầu GET tới:

localhost:5000/movies/

Điều này sẽ trả về tất cả các phim dưới dạng danh sách JSON:

[ { "_id": { "$oid": "600eb604b076cdbc347e2b99" }, "cast": [], "rated": "5", "title": "Movie 1", "year": 1998 }, { "_id": { "$oid": "600eb604b076cdbc347e2b9a" }, "cast": [], "rated": "4", "title": "Movie 2", "year": 1999 } ]

Khi bạn đang xử lý các kết quả lớn từ các truy vấn như thế này, bạn sẽ muốn cắt bớt chúng và cho phép người dùng cuối tải chậm hơn theo yêu cầu.

Flask-MongoEngine cho phép chúng tôi phân trang các kết quả rất dễ dàng:

@app.route['/movies'] def get_movies[]: page = int[request.args.get['page',1]] limit = int[request.args.get['limit',10]] movies = Movie.objects.paginate[page=page, per_page=limit] return jsonify[[movie.to_dict[] for movie in movies.items]], 200

Các Movie.objects.paginate[page=page, per_page=limit] trả về một Pagination trong đó có danh sách các bộ phim trong đó .items thuộc tính, lặp qua thuộc tính, chúng tôi nhận được phim của mình trên trang đã chọn:

[ { "_id": { "$oid": "600eb604b076cdbc347e2b99" }, "cast": [], "rated": "5", "title": "Back to The Future III", "year": 1998 }, { "_id": { "$oid": "600fb95dcb1ba5529bbc69e8" }, "cast": [], "rated": "4", "title": "Spider man", "year": 2004 }, ... ]

Nhận một tài liệu

Chúng tôi có thể lấy một Movie kết quả bằng cách chuyển id làm tham số cho Movie.objects[] phương pháp:

@app.route['/movies/'] def get_one_movie[id: str]: movie = Movie.objects[id=id].first[] return jsonify[movie], 200

Movie.objects[id=id] sẽ trả về một tập hợp tất cả các phim có id khớp với tham số và first[] trả về đầu tiên Movie trong bộ truy vấn, nếu có nhiều bộ.

Nếu chúng tôi gửi yêu cầu GET tới:

localhost:5000/movies/600eb604b076cdbc347e2b99

Chúng tôi sẽ nhận được kết quả này:

{ "_id": { "$oid": "600eb604b076cdbc347e2b99" }, "cast": [], "rated": "5", "title": "Back to The Future III", "year": 1998 }

Đối với hầu hết các trường hợp sử dụng, chúng tôi muốn nâng cao 404_NOT_FOUND lỗi nếu không có tài liệu nào khớp với tài liệu đã cung cấp id. Flask-MongoEngine đã giúp chúng tôi bao phủ first_or_404[]get_or_404[] bộ truy vấn tùy chỉnh:

@app.route['/movies/'] def get_one_movie[id: str]: movie = Movie.objects.first_or_404[id=id] return movie.to_dict[], 200

Tạo / Lưu tài liệu

MongoEngine giúp tạo tài liệu mới cực kỳ dễ dàng bằng cách sử dụng các mô hình của chúng tôi. Tất cả những gì chúng ta cần làm là gọi save[] phương thức trên cá thể lớp mô hình của chúng tôi như sau:

@app.route['/movies/', methods=["POST"]] def add_movie[]: body = request.get_json[] movie = Movie[**body].save[] return jsonify[movie], 201

**body giải nén body từ điển vào Movie như các tham số đã đặt tên. Ví dụ nếu body = {“title”: “Movie Title”, “year”: 2015},
sau đó Movie[**body] giống như Movie[title=”Movie Title”, year=2015]

Nếu chúng tôi gửi yêu cầu này đến localhost:5000/movies/:

$ curl -X POST -H "Content-Type: application/json" -d '{"title": "Spider Man 3", "year": 2009, "rated": "5"}' localhost:5000/movies/

Nó sẽ lưu và trả lại tài liệu:

{ "_id": { "$oid": "60290817f3918e990ba24f14" }, "cast": [], "director": { "$oid": "600fb8138724900858706a56" }, "rated": "5", "title": "Spider Man 3", "year": 2009 }

Tạo tài liệu với Tài liệu nhúng

Để thêm tài liệu nhúng, trước tiên chúng ta cần tạo tài liệu để nhúng, sau đó gán nó vào trường thích hợp trong mô hình phim của chúng ta:

@app.route['/movies-embed/', methods=["POST"]] def add_movie_embed[]: # Created Imdb object imdb = Imdb[imdb_, rating=4.2, votes=7.9] body = request.get_json[] # Add object to movie and save movie = Movie[imdb=imdb, **body].save[] return jsonify[movie], 201

Nếu chúng tôi gửi yêu cầu này:

$ curl -X POST -H "Content-Type: application/json" -d '{"title": "Batman", "year": 2016, "rated": "yes"}' localhost:5000/movies-embed/

Thao tác này sẽ trả về tài liệu mới được thêm vào với tài liệu được nhúng:

{ "_id": { "$oid": "601096176cc65fa421dd905d" }, "cast": [], "imdb": { "imdb_id": "12340mov", "rating": 4.2, "votes": 7 }, "rated": "yes", "title": "Batman", "year": 2016 }

Tạo tài liệu động

Vì không có trường nào được xác định trong mô hình, chúng tôi sẽ cần cung cấp bất kỳ nhóm trường tùy ý nào cho đối tượng tài liệu động của chúng tôi.

Bạn có thể nhập bất kỳ số lượng trường nào ở đây, thuộc bất kỳ loại nào. Bạn thậm chí không cần phải có các loại trường đồng nhất giữa nhiều tài liệu.

Có một số cách để đạt được điều này:

  • Chúng tôi có thể tạo đối tượng tài liệu với tất cả các trường mà chúng tôi muốn thêm vào như một yêu cầu giống như chúng tôi đã làm cho đến nay:

    @app.route['/director/', methods=['POST']] def add_dir[]: body = request.get_json[] director = Director[**body].save[] return jsonify[director], 201
  • Trước tiên, chúng ta có thể tạo đối tượng, sau đó thêm các trường bằng cách sử dụng ký hiệu dấu chấm và gọi phương thức lưu khi chúng ta hoàn tất:

    @app.route['/director/', methods=['POST']] def add_dir[]: body = request.get_json[] director = Director[] director.name = body.get["name"] director.age = body.get["age"] director.save[] return jsonify[director], 201
  • Và cuối cùng, chúng ta có thể sử dụng Python setattr[] phương pháp:

    @app.route['/director/', methods=['POST']] def add_dir[]: body = request.get_json[] director = Director[] setattr[director, "name", body.get["name"]] setattr[director, "age", body.get["age"]] director.save[] return jsonify[director], 201

Trong mọi trường hợp, chúng tôi có thể thêm bất kỳ tập hợp trường nào, dưới dạng DynamicDocument triển khai không định nghĩa bất kỳ chính nó.

Nếu chúng tôi gửi yêu cầu ĐĂNG tới localhost:5000/director/:

$ curl -X POST -H "Content-Type: application/json" -d '{"name": "James Cameron", "age": 57}' localhost:5000/director/

Kết quả này trong:

{ "_id": { "$oid": "6029111e184c2ceefe175dfe" }, "age": 57, "name": "James Cameron" }

Cập nhật tài liệu

Để cập nhật tài liệu, chúng tôi truy xuất tài liệu liên tục từ cơ sở dữ liệu, cập nhật các trường của nó và gọi update[] trên đối tượng đã sửa đổi trong bộ nhớ:

@app.route['/movies/', methods=['PUT']] def update_movie[id]: body = request.get_json[] movie = Movie.objects.get_or_404[id=id] movie.update[**body] return jsonify[str[movie.id]], 200

Hãy gửi một yêu cầu cập nhật:

$ curl -X PUT -H "Content-Type: application/json" -d '{"year": 2016}' localhost:5000/movies/600eb609b076cdbc347e2b9a/

Thao tác này sẽ trả về id của tài liệu được cập nhật:

"600eb609b076cdbc347e2b9a"

Chúng tôi cũng có thể cập nhật nhiều tài liệu cùng một lúc bằng cách sử dụng update[] phương pháp. Chúng tôi chỉ truy vấn cơ sở dữ liệu cho các tài liệu chúng tôi định cập nhật, với một số điều kiện và gọi phương thức cập nhật trên Queryset kết quả:

@app.route['/movies_many/', methods=['PUT']] def update_movie_many[title]: body = request.get_json[] movies = Movie.objects[year=year] movies.update[**body] return jsonify[[str[movie.id] for movie in movies]], 200

Hãy gửi một yêu cầu cập nhật:

$ curl -X PUT -H "Content-Type: application/json" -d '{"year": 2016}' localhost:5000/movies_many/2010/

Thao tác này sẽ trả về danh sách các ID của các tài liệu được cập nhật:

[ "60123af478a2c347ab08c32b", "60123b0989398f6965f859ab", "60123bfe2a91e52ba5434630", "602907f3f3918e990ba24f13", "602919f67e80d573ad3f15e4" ]

Xóa tài liệu

Giống như update[] phương pháp, delete[] phương thức xóa một đối tượng, dựa trên id:

@app.route['/movies/', methods=['DELETE']] def delete_movie[id]: movie = Movie.objects.get_or_404[id=id] movie.delete[] return jsonify[str[movie.id]], 200

Tất nhiên, vì chúng tôi có thể không đảm bảo rằng một đối tượng với ID đã cho có trong cơ sở dữ liệu, chúng tôi sử dụng get_or_404[] phương thức để truy xuất nó, trước khi gọi delete[].

Hãy gửi một yêu cầu xóa:

$ curl -X DELETE -H "Content-Type: application/json" localhost:5000/movies/600eb609b076cdbc347e2b9a/

Kết quả này trong:

"600eb609b076cdbc347e2b9a"

Chúng tôi cũng có thể xóa nhiều tài liệu cùng một lúc, để làm điều này, chúng tôi sẽ truy vấn cơ sở dữ liệu để tìm các tài liệu chúng tôi muốn xóa và sau đó gọi delete[] trên Queryset kết quả.

Ví dụ: để xóa tất cả các phim được thực hiện trong một năm nhất định, chúng tôi sẽ làm như sau:

@app.route['/movies/delete-by-year//', methods=['DELETE']] def delete_movie_by_year[year]: movies = Movie.objects[year=year] movies.delete[] return jsonify[[str[movie.id] for movie in movies]], 200

Hãy gửi yêu cầu xóa, xóa tất cả các mục phim trong năm 2009:

$ curl -X DELETE -H "Content-Type: application/json" localhost:5000/movies/delete-by-year/2009/

Kết quả này trong:

[ "60291fdd4756f7031638b703", "60291fde4756f7031638b704", "60291fdf4756f7031638b705" ]

Làm việc với tệp

Tạo và lưu trữ tệp

MongoEngine giúp bạn dễ dàng giao tiếp với MongoDB GridFS để lưu trữ và truy xuất tệp. MongoEngine đạt được điều này thông qua FileField[].

Hãy xem cách chúng tôi có thể tải tệp lên MongoDB GridFS bằng MongoEngine:

@app.route['/movies_with_poster', methods=['POST']] def add_movie_with_image[]: # 1 image = request.files['file'] # 2 movie = Movie[title = "movie with poster", year=2021] # 3 movie.poster.put[image, filename=image.filename] # 4 movie.save[] # 5 return jsonify[movie], 201

Hãy xem qua khối trên, từng dòng:

  1. Đầu tiên, chúng tôi nhận được một hình ảnh từ khóa file trong request.files
  2. Tiếp theo, chúng tôi tạo một Movie
  3. Không giống như các trường khác, chúng tôi không thể gán giá trị cho FileField[] bằng cách sử dụng toán tử gán thông thường, thay vào đó, chúng tôi sẽ sử dụng put[] phương pháp để gửi hình ảnh của chúng tôi. Các put[] phương thức này nhận làm đối số cho tệp sẽ được tải lên [đây phải là một đối tượng giống tệp hoặc một luồng byte], tên tệp và siêu dữ liệu tùy chọn.
  4. Để lưu tệp của chúng tôi, chúng tôi gọi save[] trên đối tượng movie, như thường lệ.
  5. Chúng tôi trả lại movie một id tham chiếu đến hình ảnh:
{ "_id": { "$oid": "60123e4d2628f541032a0900" }, "cast": [], "poster": { "$oid": "60123e4d2628f541032a08fe" }, "title": "movie with poster", "year": 2021 }

Như bạn có thể thấy từ phản hồi JSON, tệp thực sự được lưu dưới dạng tài liệu MongoDB riêng biệt và chúng tôi chỉ có một tham chiếu cơ sở dữ liệu đến nó.

Truy xuất tệp

Một khi chúng tôi đã put[] một tập tin thành một FileField[]chúng ta có thể read[] trở lại bộ nhớ, khi chúng ta đã có một đối tượng chứa trường đó. Hãy xem cách chúng tôi có thể truy xuất tệp từ tài liệu MongoDB:

from io import BytesIO from flask.helpers import send_file @app.route['/movies_with_poster//', methods=['GET']] def get_movie_image[id]: # 1 movie = Movie.objects.get_or_404[id=id] # 2 image = movie.poster.read[] content_type = movie.poster.content_type filename = movie.poster.filename # 3 return send_file[ # 4 BytesIO[image], attachment_filename=filename, mimetype=content_type], 200

Hãy cùng xem những gì được thực hiện trong các phân đoạn:

  1. Chúng tôi đã truy xuất tài liệu phim có chứa hình ảnh.
  2. Sau đó, chúng tôi lưu hình ảnh dưới dạng một chuỗi byte vào imagecó tên tệp và loại nội dung và lưu chúng vào filenamecontent_typeS.
  3. Sử dụng Flask’s send_file[] phương pháp trợ giúp, chúng tôi cố gắng gửi tệp đến người dùng nhưng vì hình ảnh là byteschúng tôi sẽ nhận được một AttributeError: ‘bytes’ object has no attribute ‘read’ như send_file[] đang mong đợi một đối tượng giống tệp, không phải byte.
  4. Để giải quyết vấn đề này, chúng tôi sử dụng BytesIO[] lớp học từ io để giải mã đối tượng byte trở lại thành một đối tượng giống tệp send_file[] có thể gửi.

Xóa tệp

Xóa tài liệu có chứa tệp sẽ không xóa tệp khỏi GridFS, vì chúng được lưu trữ dưới dạng các đối tượng riêng biệt.

Để xóa tài liệu và các tệp đi kèm của chúng, trước tiên chúng ta phải xóa tệp trước khi xóa tài liệu.

FileField[] cũng cung cấp một delete[] phương pháp mà chúng tôi có thể sử dụng để xóa nó khỏi cơ sở dữ liệu và hệ thống tệp, trước khi chúng tôi tiếp tục xóa chính đối tượng:

@app.route['/movies_with_poster//', methods=['DELETE']] def delete_movie_image[id]: movie = Movie.objects.get_or_404[id=id] movie.poster.delete[] movie.delete[] return "", 204

Sự kết luận

MongoEngine cung cấp giao diện Pythonic tương đối đơn giản nhưng giàu tính năng để tương tác với MongoDB từ ứng dụng python và Flask-MongoEngine giúp tích hợp MongoDB vào ứng dụng Flask của chúng tôi dễ dàng hơn.

Trong hướng dẫn này, chúng tôi đã khám phá một số tính năng của MongoEngine và phần mở rộng Flask của nó. Chúng tôi đã tạo một API CRUD đơn giản và sử dụng MongoDB GridFS để lưu, truy xuất và xóa các tệp bằng MongoEngine. Trong hướng dẫn này, chúng tôi đã khám phá một số tính năng của MongoEngine và phần mở rộng Flask của nó. Chúng tôi đã tạo một API CRUD đơn giản và sử dụng MongoDB GridFS để lưu, truy xuất và xóa các tệp bằng MongoEngine.

Chủ Đề