第 1 部分:將 MongoDB 與 Flask 結合使用

你好!在本系列的最後一個部分中,我們學習瞭如何建立基本的“ CRUD” 使用 pythonlist` 的 REST API 功能。但這不是現實世界應用程式的建構方式,因為如果您的伺服器重新啟動或上帝禁止崩潰,那麼您將丟失伺服器中儲存的所有資訊。為了解決這些問題(以及許多其他問題),使用了資料庫。所以,這就是我們要做的。我們將使用 MongoDB 作為我們的資料庫。

如果您剛從這部分開始,您可以在[此處]找到我們迄今為止編寫的所有程式碼(https://github.com/paurakhsharma/flask-rest-api-blog-series/tree/master/Part% 20 -%200)。

在開始之前,請確保您已在系統中安裝了 MongoDB。如果您還沒有安裝,可以安裝 Linux、[Windown](https://docs.mongodb.com/手冊/教學/install-mongodb-on-windows/)和[macOS](https://docs.mongodb.com/manual/tutorial/install-mongodb-on-os-x/)。

主要有一些流行的函式庫可以讓 MongoDB 的使用變得更容易:

1) Pymongo 是 MongoDB 的低階 Python 包裝器,使用 Pymongo 類似於直接編寫 MongoDB 查詢。

以下是使用“Pymongo”更新“id”與給定“id”相符的電影名稱的簡單範例。

db['movies'].update({'_id': id},
                    {'$set': {'name': 'My new title'}})

Pymongo 不使用任何預先定義的模式,因此它可以充分利用 MongoDB 的無模式特性。

2) MongoEngine 是物件文件映射器,它使用文件模式,使 MongoDB 的使用變得清晰、更容易。

這是使用“mongoengine”的相同範例。

Movies.objects(id=id).update(name='My new title')

「Mongoengine」對資料庫中的欄位使用預先定義架構,這限制了它使用 MongoDB 的無架構性質。

正如我們所看到的,雙方都有各自的優點和缺點。因此,請選擇最適合您的專案的一種。在本系列中,我們將學習“Mongoengine”,如果您希望我也介紹“Pymongo”,請在下面的評論部分告訴我。

為了在我們的Flask 應用程式中更好地使用Mongoengine,有一個很棒的Flask 擴展,名為[Flask-Mongengine](http://docs.mongoengine.org/projects/flask- mongoengine/en/latest/)。

那麼,讓我們開始安裝「flask-mongoengine」。

pipenv install flask-mongoengine

注意:由於flask-mongoengine 是在mongoengine 之上建造的,所以在安裝Flask-mongoengine 時會自動安裝,而且mongoengine 是在pymongo 之上建造的,所以它也會被安裝

現在,讓我們在「movie-bag」中建立一個新資料夾。我將其稱為“資料庫”。在「database」資料夾中建立一個名為「db.py」的檔案。另外,建立另一個檔案並將其命名為“models.py”

讓我們看看文件/資料夾現在是什麼樣子。

movie-bag
│   app.py
|   Pipfile
|   Pipfile.lock   
└───database
    │   db.py
    └───models.py

現在,讓我們深入探討有趣的部分。

首先,讓我們透過將以下程式碼新增至「db.py」來初始化我們的資料庫

#~movie-bag/database/db.py

from flask_mongoengine import MongoEngine

db = MongoEngine()

def initialize_db(app):
    db.init_app(app)

在這裡,我們導入了“MongoEngine”並建立了“db”物件,並定義了一個函數“initialize_db()”,我們將從“app.py”中呼叫該函數來初始化資料庫。

讓我們在“models”目錄中的“movie.py”中編寫以下程式碼

#~movie-bag/database/models.py
from .db import db

class Movie(db.Document):
    name = db.StringField(required=True, unique=True)
    casts = db.ListField(db.StringField(), required=True)
    genres = db.ListField(db.StringField(), required=True)

我們剛剛建立的是資料庫的文件。因此,使用者無法新增此處定義的其他欄位。

這裡我們可以看到「Movie」文件有三個欄位:

1)name:是一個String類型的字段,我們在這個字段上也有兩個約束。

- “必需”,這意味著用戶在不提供標題的情況下無法建立新電影。

- “唯一”,這意味著電影名稱必須是唯一的,不能重複。

2) casts:是一個list類型的字段,其中包含String類型的值

3) genres: 與casts相同

最後,我們可以在「app.py」中初始化資料庫,並更改「view」函數(處理 API 請求的函數)以使用我們先前定義的「Movie」文件。

#~movie-bag/app.py

-from flask import Flask, jsonify, request
+from flask import Flask, request, Response
+from database.db import initialize_db
+from database.models import Movie

 app = Flask(__name__)

-movies = [
-    {
-        "name": "The Shawshank Redemption",
-        "casts": ["Tim Robbins", "Morgan Freeman", "Bob Gunton", "William Sadler"],
-        "genres": ["Drama"]
-    },
-    {
-       "name": "The Godfather ",
-       "casts": ["Marlon Brando", "Al Pacino", "James Caan", "Diane Keaton"],
-       "genres": ["Crime", "Drama"]
-    }
-]
+app.config['MONGODB_SETTINGS'] = {
+    'host': 'mongodb://localhost/movie-bag'
+}
+
+initialize_db(app)

[email protected]('/movies')
-def hello():
-    return jsonify(movies)

[email protected]('/movies')
+def get_movies():
+    movies = Movie.objects().to_json()
+    return Response(movies, mimetype="application/json", status=200)

[email protected]('/movies', methods=['POST'])
-def add_movie():
-    movie = request.get_json()
-    movies.append(movie)
-    return {'id': len(movies)}, 200

[email protected]('/movies', methods=['POST'])
+    body = request.get_json()
+    movie = Movie(**body).save()
+    id = movie.id
+    return {'id': str(id)}, 200

[email protected]('/movies/<int:index>', methods=['PUT'])
-def update_movie(index):
-    movie = request.get_json()
-    movies[index] = movie
-    return jsonify(movies[index]), 200

[email protected]('/movies/<id>', methods=['PUT'])
+def update_movie(id):
+    body = request.get_json()
+    Movie.objects.get(id=id).update(**body)
+    return '', 200

[email protected]('/movies/<int:index>', methods=['DELETE'])
-def delete_movie(index):
-    movies.pop(index)
-    return 'None', 200

[email protected]('/movies/<id>', methods=['DELETE'])
+def delete_movie(id):
+    Movie.objects.get(id=id).delete()
+    return '', 200

 app.run()

哇!變化很多,讓我們一步一步地進行變化。

-from flask import Flask, jsonify, request
+from flask import Flask, request, Response
+from database.db import initialize_db
+from database.models.movie import Movie

這裡我們刪除了“jsonify”,因為我們不再需要,並加入了“Response”,我們用它來設定回應的類型。然後我們從之前定義的「db.py」導入「initialize_db」來初始化資料庫。最後,我們從“movie.py”導入“Movie”文件

+app.config['MONGODB_SETTINGS'] = {
+    'host': 'mongodb://localhost/movie-bag'
+}
+
+db = initialize_db(app)

這裡我們設定 mongodb 資料庫的配置。這裡主機的格式為「<host-url>/<database-name>」。由於我們已經在本地安裝了 mongodb,因此我們可以從“mongodb://localhost/”存取它,並且我們將資料庫命名為“movie-bag”。

最後,我們初始化資料庫。

[email protected]('/movies')
+def get_movies():
+    movies = Movie.objects().to_json()
+    return Response(movies, mimetype="application/json", status=200)
+

在這裡,我們使用“Movies.objects()”從“Movie”文件中獲取所有物件,並使用“to_json()”將它們轉換為“JSON”。最後,我們傳回一個「Response」物件,其中我們將回應類型定義為「application/json」。

[email protected]('/movies', methods=['POST'])
+    body = request.get_json()
+    movie = Movie(**body).save()
+    id = movie.id
+    return {'id': str(id)}, 200

在「POST」請求中,我們首先取得發送的「JSON」和一個請求。然後我們使用“Movie(body)”請求中的欄位來載入“Movie”文件。這裡的「」稱為擴充運算符,在 JavaScript 中寫為「...」(如果您熟悉的話)。顧名思義,它的作用是傳播「dict」物件。 <br/>

所以,Movie(**body) 變成了

Movie(name="Name of the movie",
    casts=["a caste"],
    genres=["a genre"])

最後,我們保存文件並獲取其“id”,我們將其作為回應返回。

[email protected]('/movies/<id>', methods=['PUT'])
+def update_movie(id):
+    body = request.get_json()
+    Movie.objects.get(id=id).update(**body)
+    return '', 200

這裡我們先找到與請求中發送的「id」相符的Movie文件,然後更新它。這裡我們也應用了擴充運算子將值傳遞給「update()」函數。

[email protected]('/movies/<id>', methods=['DELETE'])
+def delete_movie(id):
+    Movie.objects.get(id=id).delete()
+    return '', 200

與此處的“update_movie()”類似,我們獲取與給定“id”匹配的電影文件並將其從資料庫中刪除。

哦,我剛剛想起來,我們還沒有將 API 端點加入到“GET”,僅從我們的伺服器獲取一個文件。

讓我們加入它:

app.run() 上方加入以下程式碼

@app.route('/movies/<id>')
def get_movie(id):
    movies = Movie.objects.get(id=id).to_json()
    return Response(movies, mimetype="application/json", status=200)

現在您可以從 API 端點「/movies/<valid_id>」取得單一影片。

要執行伺服器,請確保您位於“movie-bag”目錄。

然後執行

pipenv shell
python app.py

在終端機中啟動虛擬環境並啟動伺服器。

哇!恭喜您已經走到這一步了。要測試API,請使用我們在上一篇 中使用的“ Postman」本系列的一部分。

您可能已經注意到,如果我們向端點發送無效資料,例如:沒有名稱或其他字段,我們會收到“HTML”形式的不友善錯誤。如果我們嘗試取得資料庫中不存在的「id」影片文件,那麼我們也會收到「HTML」回應形式的不友善錯誤。這並不是一個精心建構的 API 的例外行為。我們將在本系列的後面部分中了解如何處理此類錯誤。

我們從本系列的這一部分學到了什麼?

  • PymongoMongoengine 之間的差異。

  • 如何使用「Mongoengine」建立文件架構。

  • 如何使用「Mongoengine」執行「CRUD」操作。

  • Python 擴充運算子。

您可以在[此處]找到這部分的完整程式碼(https://github.com/paurakhsharma/flask-rest-api-blog-series/tree/master/Part%20-%201)

在下一部分中,我們將學習如何使用「Blueprint」來更好地建立 Flask 應用程式。以及如何使用“flask-restful”以最少的設定遵循最佳實踐,更快地建立 REST API

直到那時快樂編碼😊


原文出處:https://dev.to/paurakhsharma/flask-rest-api-part-1-using-mongodb-with-flask-3g7d


共有 0 則留言