وبلاگ رسانگار
با ما حرفه ای باشید

سرور مجازی NVMe

راهنمای Flask-MongoEngine در پایتون

0 1
زمان لازم برای مطالعه: 9 دقیقه


معرفی

ساختن یک برنامه وب تقریباً همیشه به معنای پرداختن به داده های یک پایگاه داده است. بسته به پایگاه داده های مختلفی برای انتخاب وجود دارد روی ترجیح شما

در این راهنما، ما باید به روش ادغام یکی از محبوب ترین پایگاه داده های NoSQL نگاهی بیندازیم – MongoDB – با میکروفریمورک Flask.

در این راهنما، روش ادغام MongoDB با Flask با استفاده از یک کتابخانه محبوب را بررسی خواهیم کرد – MongoEngineو به طور خاص، لفاف آن – Flask-MongoEngine.

از طرف دیگر، می توانید MongoDB را با Flask-PyMongo ادغام کنید.

Flask-MongoEngine

MongoEngine یک ODM (نقشه‌نگار سند شی) که کلاس‌های پایتون (مدل‌ها) را به اسناد MongoDB نگاشت می‌کند و ایجاد و دستکاری اسناد را به صورت برنامه‌نویسی مستقیماً از کد ما آسان می‌کند.

راه اندازی و پیکربندی

برای بررسی برخی از ویژگی های MongoEngine، یک API فیلم ساده ایجاد می کنیم که به ما امکان می دهد عملیات CRUD را انجام دهیم. روی Movie نمونه ها

برای شروع، اجازه دهید Flask را نصب کنیم اگر قبلاً آن را ندارید:

$ pip install flask

در مرحله بعد، ما به یک نمونه MongoDB نیاز داریم، MongoDB یک نمونه ابری را فراهم می کند – MongoDB Atlas – که می توانیم به صورت رایگان از آن استفاده کنیم، با این حال، از یک نمونه نصب شده محلی استفاده خواهیم کرد. دستورالعمل‌های دریافت و نصب MongoDB را می‌توانید در این قسمت پیدا کنید اسناد رسمی.

و با انجام این کار، ما همچنین می خواهیم کتابخانه Flask-MongoEngine را نصب کنیم:

$ pip install flask-mongoengine

اتصال به یک نمونه پایگاه داده MongoDB

اکنون که Flask و Flask-MongoEngine را نصب کرده ایم، باید برنامه Flask خود را با یک نمونه MongoDB متصل کنیم.

ما با وارد کردن Flask و Flask-MongoEngine به برنامه خود شروع خواهیم کرد:

from flask import Flask
from flask_mongoengine import MongoEngine

سپس، می توانیم شی برنامه Flask را ایجاد کنیم:

app = Flask(__name__)

که ما از آن برای مقداردهی اولیه a استفاده خواهیم کرد MongoEngine هدف – شی. اما قبل از اینکه مقداردهی اولیه انجام شود، به نمونه MongoDB خود نیاز داریم.

این مرجع یک کلید در است app.config که مقدار آن یک فرهنگ لغت حاوی پارامترهای اتصال است:

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

ما همچنین می‌توانیم یک URI اتصال ارائه کنیم:

app.config('MONGODB_SETTINGS') = {
    'host':'mongodb://localhost/db_name'
}

با انجام تنظیمات، اکنون می توانیم a را مقداردهی اولیه کنیم MongoEngine هدف – شی:

db = MongoEngine(app)

ما همچنین می توانیم استفاده کنیم init_app() روش از MongoEngine شی برای مقداردهی اولیه:

db = MongoEngine()
db.init_app(app)

هنگامی که پیکربندی و مقداردهی اولیه انجام شد، می توانیم برخی از ویژگی های شگفت انگیز MongoEngine را بررسی کنیم.

ایجاد کلاس های مدل

به عنوان یک ODM، MongoEngine از کلاس های پایتون برای نمایش اسناد در پایگاه داده ما استفاده می کند.

MongoEngine انواع مختلفی از کلاس های اسناد را ارائه می دهد:

  1. سند
  2. EmbeddedDocument
  3. Dynamic Document
  4. DynamicEmbeddedDocument

سند

این نشان دهنده سندی است که مجموعه خود را در پایگاه داده دارد که با ارث بردن از آن ایجاد شده است mongoengine.Document یا از ما MongoEngine نمونه، مثال (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 همچنین کلاس‌های اضافی را ارائه می‌کند که نوع داده‌هایی را که فیلدهای یک سند باید بگیرند و تأیید می‌کنند و اصلاح‌کننده‌های اختیاری برای اضافه کردن جزئیات یا محدودیت‌های بیشتر به هر فیلد ارائه می‌کند.

نمونه هایی از فیلدها عبارتند از:

  1. StringField() برای مقادیر رشته
  2. IntField() برای مقادیر int
  3. ListField() برای یک لیست
  4. FloatField() برای مقادیر ممیز شناور
  5. ReferenceField() برای ارجاع به اسناد دیگر
  6. EmbeddedDocumentField() برای اسناد جاسازی شده و غیره
  7. FileField() برای ذخیره فایل ها (بیشتر روی این بعدا)

همچنین می توانید اصلاح کننده هایی را در این زمینه ها اعمال کنید، مانند:

  • required
  • default
  • unique
  • primary_key و غیره.

با تنظیم هر یک از اینها به True، آنها به طور خاص در آن زمینه اعمال خواهند شد.

EmbeddedDocument

این نشان دهنده سندی است که مجموعه خود را در پایگاه داده ندارد، اما در سند دیگری جاسازی شده است، با ارث بردن از EmbeddedDocument کلاس:

class Imdb(db.EmbeddedDocument):
    imdb_id = db.StringField()
    rating = db.DecimalField()
    votes = db.IntField()

Dynamic Document

این سندی است که فیلدهای آن به صورت پویا و با بهره‌گیری از ماهیت پویا MongoDB اضافه می‌شوند.

مانند انواع دیگر اسناد، MongoEngine کلاسی را برای DynamicDocument:

class Director(db.DynamicDocument):
    pass

DynamicEmbeddedDocument

این همه خواص از DynamicDocument و EmbeddedDocument

class Cast(db.DynamicEmbeddedDocument):
    pass

از آنجایی که ما ساخت تمام کلاس های داده خود را به پایان رساندیم، زمان آن رسیده است که برخی از ویژگی های MongoEngine را شروع کنیم.

دسترسی به اسناد

MongoEngine پرس و جو از پایگاه داده ما را بسیار آسان می کند، ما می توانیم تمام فیلم های موجود در پایگاه داده را به این صورت دریافت کنیم.

from flask import jsonify

@app.route('/movies')
def  get_movies():
    movies = Movie.objects()
    return  jsonify(movies), 200

اگر درخواست GET را به این آدرس ارسال کنیم:

localhost:5000/movies/

این همه فیلم ها را به عنوان یک لیست JSON برمی گرداند:

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

هنگامی که با نتایج بزرگ از پرس و جوهایی مانند این سروکار دارید، باید آنها را کوتاه کنید و به کاربر نهایی اجازه دهید به آرامی موارد بیشتری را در صورت نیاز بارگذاری کند.

Flask-MongoEngine به ما اجازه می دهد تا نتایج را به راحتی صفحه بندی کنیم:

@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

این Movie.objects.paginate(page=page, per_page=limit) برمی گرداند a Pagination شی که شامل لیست فیلم های موجود در آن است .items ملک، با تکرار از طریق ملک، فیلم های خود را دریافت می کنیم روی انتخاب شده page:

(
    {
        "_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
    },
...
)

گرفتن یک سند

ما می توانیم یک تک را بازیابی کنیم Movie نتیجه با ارسال id به عنوان پارامتر به Movie.objects() روش:

@app.route('/movies/<id>')
def get_one_movie(id: str):
    movie = Movie.objects(id=id).first()
    return jsonify(movie), 200

Movie.objects(id=id) مجموعه ای از تمام فیلم هایی که id مطابق با پارامتر و first() اولی را برمی گرداند Movie اگر چندین مورد وجود داشته باشد، در مجموعه queryset قرار دارد.

اگر درخواست GET را به این آدرس ارسال کنیم:

localhost:5000/movies/600eb604b076cdbc347e2b99

به این نتیجه خواهیم رسید:

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

برای بیشتر موارد استفاده، ما می‌خواهیم a را مطرح کنیم 404_NOT_FOUND اگر هیچ سندی با سند ارائه شده مطابقت نداشته باشد، خطا می کند id. Flask-MongoEngine ما را تحت پوشش خود قرار داده است first_or_404() و get_or_404() مجموعه پرسش های سفارشی:

@app.route('/movies/<id>')
def get_one_movie(id: str):
    movie = Movie.objects.first_or_404(id=id)
    return movie.to_dict(), 200

ایجاد/ذخیره اسناد

MongoEngine ایجاد اسناد جدید با استفاده از مدل های ما را بسیار آسان می کند. تنها کاری که باید انجام دهیم این است که با save() روش روی نمونه کلاس مدل ما به شرح زیر است:

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

**body بسته بندی را باز می کند body فرهنگ لغت به Movie شیء به عنوان پارامترهای نامگذاری شده به عنوان مثال اگر body = {"title": "Movie Title", "year": 2015}، سپس Movie(**body) معادل است Movie(title="Movie Title", year=2015)

اگر این درخواست را به localhost:5000/movies/:

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

سند را ذخیره و برمی گرداند:

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

ایجاد اسناد با EmbeddedDocuments

برای افزودن یک سند جاسازی شده، ابتدا باید سندی را برای جاسازی ایجاد کنیم، سپس آن را به فیلد مناسب در مدل فیلم خود اختصاص دهیم:

@app.route('/movies-embed/', methods=("POST"))
def add_movie_embed():
    
    imdb = Imdb(imdb_id="12340mov", rating=4.2, votes=7.9)
    body = request.get_json()
    
    movie = Movie(imdb=imdb, **body).save()
    return jsonify(movie), 201

اگر این درخواست را ارسال کنیم:

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

این سند جدید اضافه شده را با سند جاسازی شده برمی گرداند:

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

ایجاد اسناد پویا

از آنجایی که هیچ فیلدی در مدل تعریف نشده است، باید هر مجموعه دلخواه از فیلدها را به شی سند پویا خود ارائه دهیم.

شما می توانید هر تعداد فیلد را در اینجا قرار دهید، از هر نوع. حتی لازم نیست انواع فیلدها بین چندین سند یکسان باشد.

چند راه برای رسیدن به این هدف وجود دارد:

  • می‌توانیم شی سند را با تمام فیلدهایی که می‌خواهیم اضافه کنیم، ایجاد کنیم، گویی درخواستی مانند آنچه تاکنون انجام داده‌ایم:

    @app.route('/director/', methods=('POST'))
    def add_dir():
        body = request.get_json()
        director = Director(**body).save()
        return jsonify(director), 201
    
  • می‌توانیم ابتدا شی را ایجاد کنیم، سپس فیلدها را با استفاده از علامت نقطه اضافه کنیم و پس از اتمام کار، متد ذخیره را فراخوانی کنیم:

    @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
    
  • و در نهایت، می توانیم از پایتون استفاده کنیم setattr() روش:

    @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
    

در هر صورت، ما می توانیم هر مجموعه ای از فیلدها را به عنوان یک اضافه کنیم DynamicDocument پیاده سازی هیچ کدام را خودش تعریف نمی کند.

اگر درخواست POST را به localhost:5000/director/:

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

این منجر به:

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

به روز رسانی اسناد

برای به روز رسانی یک سند، سند دائمی را از پایگاه داده بازیابی می کنیم، فیلدهای آن را به روز می کنیم و آن را فراخوانی می کنیم update() روش روی شی تغییر یافته در حافظه:

@app.route('/movies/<id>', 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

بیایید یک درخواست به روز رسانی ارسال کنیم:

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

این شناسه سند به روز شده را برمی گرداند:

"600eb609b076cdbc347e2b9a"

همچنین می‌توانیم بسیاری از اسناد را به‌طور همزمان با استفاده از آن به‌روزرسانی کنیم update() روش. ما فقط از پایگاه داده برای اسنادی که قصد به روز رسانی آنها را داریم، با توجه به شرایطی، پرس و جو می کنیم و روش به روز رسانی را فراخوانی می کنیم روی Queryset حاصل:

@app.route('/movies_many/<title>', 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

بیایید یک درخواست به روز رسانی ارسال کنیم:

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

با این کار فهرستی از شناسه‌های اسناد به‌روز شده را نشان می‌دهد:

(
  "60123af478a2c347ab08c32b", 
  "60123b0989398f6965f859ab", 
  "60123bfe2a91e52ba5434630", 
  "602907f3f3918e990ba24f13", 
  "602919f67e80d573ad3f15e4"
)

حذف اسناد

بسیار شبیه به update() روش، delete() متد یک شی را بر اساس حذف می کند روی آن id رشته:

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

البته، از آنجایی که ممکن است تضمینی نداشته باشیم که یک شی با شناسه داده شده در پایگاه داده وجود دارد، از آن استفاده می کنیم get_or_404() روش بازیابی آن، قبل از فراخوانی delete().

بیایید یک درخواست حذف ارسال کنیم:

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

این منجر به:

"600eb609b076cdbc347e2b9a"

همچنین می‌توانیم بسیاری از اسناد را به طور همزمان حذف کنیم. برای انجام این کار، از پایگاه داده اسنادی را که می‌خواهیم حذف کنیم پرس و جو می‌کنیم و سپس آن را فراخوانی می‌کنیم delete() روش روی Queryset حاصل

برای مثال برای حذف همه فیلم‌های ساخته شده در یک سال خاص، کاری شبیه به زیر انجام می‌دهیم:

@app.route('/movies/delete-by-year/<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

بیایید یک درخواست حذف ارسال کنیم و همه ورودی‌های فیلم سال را حذف کنیم 2009:

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

این منجر به:

(
  "60291fdd4756f7031638b703", 
  "60291fde4756f7031638b704", 
  "60291fdf4756f7031638b705"
)

کار با فایل ها

ایجاد و ذخیره فایل ها

MongoEngine ارتباط با MongoDB GridFS را برای ذخیره و بازیابی فایل ها بسیار آسان می کند. MongoEngine از طریق خود به این امر دست می یابد FileField().

بیایید نگاهی به روش آپلود فایل در MongoDB GridFS با استفاده از MongoEngine بیندازیم:

@app.route('/movies_with_poster', methods=('POST'))
def add_movie_with_image():
    
    image = request.files('file')
    
    movie = Movie(title = "movie with poster", year=2021)
    
    movie.poster.put(image, filename=image.filename)
    
    movie.save()
    
    return jsonify(movie), 201

بیایید از بلوک بالا خط به خط عبور کنیم:

  1. ابتدا یک تصویر از کلید دریافت می کنیم file که در request.files
  2. بعد یک را ایجاد می کنیم Movie هدف – شی
  3. برخلاف سایر فیلدها، ما نمی توانیم مقداری را به the اختصاص دهیم FileField() با استفاده از عملگر انتساب معمولی، در عوض، ما از آن استفاده خواهیم کرد put() روش ارسال تصویر ما این put() متد فایلی را که قرار است آپلود شود (این باید یک شی فایل مانند یا یک جریان بایت باشد)، نام فایل و ابرداده اختیاری را به عنوان آرگومان می گیرد.
  4. برای ذخیره فایل خود، با شماره تماس می گیریم save() روش روی شی فیلم، طبق معمول.
  5. را برمی گردانیم movie شی با یک شناسه ارجاع به تصویر:
{
  "_id": {
      "$oid": "60123e4d2628f541032a0900"
  },
  "cast": (),
  "poster": {
      "$oid": "60123e4d2628f541032a08fe"
  },
  "title": "movie with poster",
  "year": 2021
}

همانطور که از پاسخ JSON می بینید، فایل در واقع به عنوان یک سند جداگانه MongoDB ذخیره می شود و ما فقط یک مرجع پایگاه داده به آن داریم.

بازیابی فایل ها

یک بار ما put() یک فایل به a FileField()، ما میتوانیم read() هنگامی که یک شی حاوی آن فیلد را داشته باشیم، آن را به حافظه بازگردانیم. بیایید نگاهی به روش بازیابی فایل ها از اسناد MongoDB بیندازیم:

from io import BytesIO 
from flask.helpers import send_file

@app.route('/movies_with_poster/<id>/', methods=('GET'))
def get_movie_image(id):
    
    
    movie = Movie.objects.get_or_404(id=id)
    
    image = movie.poster.read()
    content_type = movie.poster.content_type
    filename = movie.poster.filename
    
    return send_file(
        
        BytesIO(image), 
        attachment_filename=filename, 
        mimetype=content_type), 200

بیایید نگاهی به آنچه در بخش‌ها انجام می‌شود بیندازیم:

  1. ما سند فیلم حاوی یک تصویر را بازیابی کردیم.
  2. سپس تصویر را به صورت رشته ای از بایت ها در قسمت ذخیره کردیم image متغیر، نام فایل و نوع محتوا را دریافت کرد و آنها را در آن ذخیره کرد filename و content_type متغیرها
  3. با استفاده از Flask’s send_file() به روش helper سعی می کنیم فایل را برای کاربر ارسال کنیم اما از آنجایی که تصویر یک است bytes شی، ما یک دریافت می کنیم AttributeError: 'bytes' object has no attribute 'read' مانند send_file() منتظر یک شیء فایل مانند است، نه بایت.
  4. برای حل این مشکل از BytesIO() کلاس از io ماژول برای رمزگشایی شی بایت به یک شی فایل مانند که send_file() می تواند ارسال کند.

حذف فایل ها

حذف اسناد حاوی فایل ها، فایل را از GridFS حذف نمی کند، زیرا آنها به عنوان اشیاء جداگانه ذخیره می شوند.

برای حذف اسناد و فایل های همراه آنها، ابتدا باید فایل را قبل از حذف سند حذف کنیم.

FileField() الف را نیز فراهم می کند delete() روشی که می توانیم به سادگی آن را از پایگاه داده و سیستم فایل حذف کنیم، قبل از اینکه خود شیء را حذف کنیم:

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

نتیجه

MongoEngine یک رابط پایتونیک نسبتا ساده اما پر از ویژگی برای تعامل با MongoDB از یک python نرم افزار و Flask-MongoEngine ادغام MongoDB را در برنامه های Flask ما آسان تر می کند.

در این راهنما، برخی از ویژگی های MongoEngine و پسوند Flask آن را بررسی کرده ایم. ما یک CRUD API ساده ایجاد کرده‌ایم و از MongoDB GridFS برای ذخیره، بازیابی و حذف فایل‌ها با استفاده از MongoEngine استفاده کرده‌ایم. در این راهنما، برخی از ویژگی‌های MongoEngine و پسوند Flask آن را بررسی کرده‌ایم. ما یک CRUD API ساده ایجاد کرده‌ایم و از MongoDB GridFS برای ذخیره، بازیابی و حذف فایل‌ها با استفاده از MongoEngine استفاده کرده‌ایم.

(برچسب‌ها به ترجمه)# python



منتشر شده در 1403-01-12 08:32:04

امتیاز شما به این مطلب
دیدگاه شما در خصوص مطلب چیست ؟

آدرس ایمیل شما منتشر نخواهد شد.

لطفا دیدگاه خود را با احترام به دیدگاه های دیگران و با توجه به محتوای مطلب درج کنید