Интеграция MongoDB с Flask с помощью Flask-PyMongo

Введение Создание веб-приложения почти всегда означает работу с данными из базы данных. Существуют различные базы данных на выбор, в зависимости от ваших предпочтений. В этой статье мы рассмотрим, как интегрировать одну из самых популярных баз данных NoSQL - MongoDB - с микро-фреймворком Flask. Существует несколько расширений Flask для интеграции MongoDB, здесь мы будем использовать расширение Flask-PyMongo. Мы также будем работать над простым API Todo-List, чтобы изучить возможности CRUD.

Вступление

Создание веб-приложения почти всегда означает работу с данными из базы данных. Существуют различные базы данных на выбор, в зависимости от ваших предпочтений.

В этой статье мы рассмотрим, как интегрировать одну из самых популярных баз данных NoSQL - MongoDB - с микро-фреймворком Flask.

Существует несколько расширений Flask для интеграции MongoDB , здесь мы будем использовать расширение Flask-PyMongo.

Мы также будем работать над простым API списка Todo-List, чтобы изучить возможности MongoDB CRUD.

Установка и конфигурация

Чтобы следовать этому руководству, вам понадобится доступ к экземпляру MongoDB, вы можете получить его из MongoDB Atlas или использовать локальный экземпляр. Мы будем использовать локальный экземпляр на нашей личной машине.

Чтобы установить локальный экземпляр MongoDB, перейдите на их официальный сайт документации, чтобы получить инструкции по его загрузке и установке.

Вам также потребуется установить Flask , а если вы этого не сделаете, вы можете сделать это с помощью следующей команды:

 $ pip install flask 

Затем нам нужно настроить Flask-PyMongo , который является оболочкой для пакета PyMongo python.

PyMongo - это низкоуровневая оболочка для MongoDB, она использует команды, аналогичные командам MongoDB CLI для:

  1. Создание данных
  2. Доступ к данным
  3. Изменение данных

Он не использует какую-либо предопределенную схему, поэтому может в полной мере использовать бессхемный характер MongoDB.

Чтобы начать использовать Flask-PyMongo, нам нужно установить его с помощью следующей команды.

 $ pip install Flask-PyMongo 

Теперь, когда все готово, давайте приступим к интеграции MongoDB в наше приложение Flask.

Подключение к экземпляру базы данных MongoDB с помощью Flask

Прежде чем мы фактически выполним какую-либо работу, мы хотим подключить наш экземпляр MongoDB к приложению Flask. Начнем с импорта Flask и Flask-PyMongo в наше приложение:

 from flask_pymongo import PyMongo 
 import flask 

Далее мы создадим объект приложения Flask:

 app = flask.Flask(__name__) 

Который мы затем будем использовать для инициализации нашего клиента MongoDB. Конструктор PyMongo (импортированный из flask_pymongo ) принимает наш объект приложения Flsk и строку URI базы данных.

Это связывает наше приложение с экземпляром MongoDB:

 mongodb_client = PyMongo(app, uri="mongodb://localhost:27017/todo_db") 
 db = mongodb_client.db 

Строка URI также может быть назначена ключу MONGO_URI в app.config

 app.config["MONGO_URI"] = "mongodb://localhost:27017/todo_db" 
 mongodb_client = PyMongo(app) 
 db = mongodb_client.db 

Как только приложение подключится к экземпляру, мы можем приступить к реализации функций CRUD приложения.

Создание документов - добавление новых элементов в базу данных

MongoDB работает с коллекциями, которые аналогичны обычной таблице SQL. Поскольку мы делаем приложение со списком задач, у нас будет коллекция todos Для ссылки на него мы используем объект db Каждая сущность - это документ , а собрание - это действительно собрание документов.

Для того, чтобы вставить новую запись в нашей todos коллекции, мы используем db.colection.insert_one() метод. MongoDB естественно работает с Python, учитывая его синтаксис для вставки, запроса и удаления.

При вставке документа в коллекцию MongoDB вы должны указать словарь с помощью <field> и <value> . Чтобы вставить документ в коллекцию MongoDB с использованием Python в качестве посредника, вы передадите словари, встроенные в Python.

Таким образом, чтобы вставить новую сущность, мы сделаем что-то вроде:

 @app.route("/add_one") 
 def add_one(): 
 db.todos.insert_one({'title': "todo title", 'body': "todo body"}) 
 return flask.jsonify(message="success") 

Мы также можем добавить несколько записей одновременно, используя метод db.colection.insert_many() . Метод insert_many() берет список словарей и добавляет их в коллекцию:

 @app.route("/add_many") 
 def add_many(): 
 db.todos.insert_many([ 
 {'_id': 1, 'title': "todo title one ", 'body': "todo body one "}, 
 {'_id': 2, 'title': "todo title two", 'body': "todo body two"}, 
 {'_id': 3, 'title': "todo title three", 'body': "todo body three"}, 
 {'_id': 4, 'title': "todo title four", 'body': "todo body four"}, 
 {'_id': 5, 'title': "todo title five", 'body': "todo body five"}, 
 {'_id': 1, 'title': "todo title six", 'body': "todo body six"}, 
 ]) 
 return flask.jsonify(message="success") 

Если мы попытаемся добавить повторяющуюся запись, BulkWriteError , что означает, что будут вставлены только записи до указанного дубликата, а все, что находится после дубликата, будет потеряно, поэтому имейте это в виду при попытке вставить много документов.

Если мы хотим вставить в наш список только действительные и уникальные записи, нам нужно будет установить для ordered параметра метода insert_many() значение false а затем перехватить исключение BulkWriteError

 from pymongo.errors import BulkWriteError 
 
 @app.route("/add_many") 
 def add_many(): 
 try: 
 todo_many = db.todos.insert_many([ 
 {'_id': 1, 'title': "todo title one ", 'body': "todo body one "}, 
 {'_id': 8, 'title': "todo title two", 'body': "todo body two"}, 
 {'_id': 2, 'title': "todo title three", 'body': "todo body three"}, 
 {'_id': 9, 'title': "todo title four", 'body': "todo body four"}, 
 {'_id': 10, 'title': "todo title five", 'body': "todo body five"}, 
 {'_id': 5, 'title': "todo title six", 'body': "todo body six"}, 
 ], ordered=False) 
 except BulkWriteError as e: 
 return flask.jsonify(message="duplicates encountered and ignored", 
 details=e.details, 
 inserted=e.details['nInserted'], 
 duplicates=[x['op'] for x in e.details['writeErrors']]) 
 
 return flask.jsonify(message="success", insertedIds=todo_many.inserted_ids) 

Этот подход вставит все действительные документы в коллекцию MongoDB. Кроме того, он регистрирует сведения о неудачных добавлениях и распечатывает их обратно пользователю в виде сообщения JSON.

Мы сделали это с помощью jsonify() , который принимает сообщение, которое мы хотели бы вернуть, а также дополнительные параметры, которые позволяют нам настроить его для целей ведения журнала.

Наконец, мы возвращаем успешные вставки почти таким же образом.

Чтение документов - получение данных из базы данных

Flask-PyMongo предоставляет несколько методов (расширенных из PyMongo) и несколько вспомогательных методов для получения данных из базы данных.

Чтобы получить все документы из todos коллекции, мы будем использовать db.collection.find() метод.

Этот метод вернет список всех todos в нашей базе данных. Подобно find() , метод find_one() возвращает один документ по его идентификатору.

Начнем с find() :

 @app.route("/") 
 def home(): 
 todos = db.todos.find() 
 return flask.jsonify([todo for todo in todos]) 

Метод find() также может принимать необязательный параметр фильтра. Этот параметр фильтра представлен словарем, в котором указаны свойства, которые мы ищем. Если вы раньше работали с MongoDB, вы, вероятно, знакомы с тем, как выглядят их запросы и компараторы.

Если нет, то вот как мы можем использовать словарь Python для соответствия формату запроса MongoDB:

 # Query document where the `id` field is `3` 
 {"id":3} 
 
 # Query document where both `id` is `3` and `title` is `Special todo` 
 {"id":3, "title":"Special todo"} 
 
 # Query using special operator - Greater than Or Equal To, denoted with 
 # the dollar sign and name ($gte) 
 {"id" : {$gte : 5}} 

Некоторые другие специальные операторы включают операторы $eq , $ne , $gt , $lt , $lte и $nin .

Если вы не знакомы с ними, отличное место, чтобы узнать о них больше, - это официальная документация .

Теперь, когда мы рассмотрели задание запросов MongoDB для фильтрации find() , давайте посмотрим, как получить один документ с учетом его _id :

 @app.route("/get_todo/<int:todoId>") 
 def insert_one(todoId): 
 todo = db.todos.find_one({"_id": todoId}) 
 return todo 

Итак, если бы мы отправили GET запрос на http://localhost:5000/get_todo/5 , мы бы получили следующий результат:

 { 
 "_id": 5, 
 "body": "todo body six", 
 "title": "todo title six" 
 } 

Обратите внимание, что 5000 - это порт сервера Flask по умолчанию, но его можно легко изменить при создании объекта приложения Flask.

В большинстве случаев мы хотели бы получить элемент или вернуть 404 если элемент не был найден.

Flask-PyMongo предоставляет для этого вспомогательную функцию, метод find_one_or_404() который 404 если запрошенный ресурс не был найден.

Обновить и заменить документы

Чтобы обновить записи в нашей базе данных, мы можем использовать метод update_one() или replace_one() чтобы изменить значение существующей сущности.

replace_one() имеет следующие аргументы:

  1. filter - запрос, который определяет, какие записи будут заменены.
  2. replacement - записи, которые будут заменены при замене.
  3. {} - объект конфигурации, у которого есть несколько параметров, из которых мы должны сосредоточиться - upsert .

upsert , если установлено значение true будет вставлена replacement как новый документ, если в базе данных нет совпадений с фильтрами. А если есть совпадения, то он replacement их. Если upsert если false, и вы попытаетесь обновить несуществующий документ, ничего не произойдет.

Давайте посмотрим, как мы можем обновлять документы:

 @app.route("/replace_todo/<int:todoId>") 
 def replace_one(todoId): 
 result = db.todos.replace_one({'_id': todoId}, {'title': "modified title"}) 
 return {'id': result.raw_result} 
 
 @app.route("/update_todo/<int:todoId>") 
 def update_one(todoId): 
 result = db.todos.update_one({'_id': todoId}, {"$set": {'title': "updated title"}}) 
 return result.raw_result 

Итак, если бы мы отправили запрос на http://localhost:5000/update_todo/5 , мы бы получили следующий результат:

 { 
 "id": { 
 "n": 1, 
 "nModified": 1, 
 "ok": 1.0, 
 "updatedExisting": true 
 } 
 } 

Точно так же, если бы мы тоже отправили запрос на http://localhost:5000/replace_todo/5 , мы бы получили следующий результат:

 { 
 "id": { 
 "n": 1, 
 "nModified": 1, 
 "ok": 1.0, 
 "updatedExisting": true 
 } 
 } 

Блок кода вернет UpdatedResult , с которым может быть утомительно работать. Вот почему Flask-PyMongo предоставляет более удобные методы, такие как find_one_and_update() и find_one_and_replace() , которые обновят запись и вернут эту запись:

 @app.route("/replace_todo/<int:todoId>") 
 def replace_one(todoId): 
 todo = db.todos.find_one_and_replace({'_id': todoId}, {'title': "modified title"}) 
 return todo 
 
 @app.route("/update_todo/<int:todoId>") 
 def update_one(todoId): 
 result = db.todos.find_one_and_update({'_id': todoId}, {"$set": {'title': "updated title"}}) 
 return result 

Итак, если бы мы отправили запрос на http://localhost:5000/update_todo/5 , мы бы получили следующий результат:

 { 
 "_id": 5, 
 "title": "updated title" 
 } 

Точно так же, если бы мы тоже отправили запрос на http://localhost:5000/replace_todo/5 , мы бы получили следующий результат:

 { 
 "_id": 5, 
 "title": "modified title" 
 } 

Flask-PyMongo также позволяет выполнять массовые обновления с помощью метода update_many()

 @app.route('/update_many') 
 def update_many(): 
 todo = db.todos.update_many({'title' : 'todo title two'}, {"$set": {'body' : 'updated body'}}) 
 return todo.raw_result 

Приведенный выше блок кода найдет и обновит все записи с заголовком «todo title two» и приведет к:

Отправка запроса на наши новоиспеченные точки возвращает следующий результат:

 { 
 "n": 1, 
 "nModified": 1, 
 "ok": 1.0, 
 "updatedExisting": true 
 } 

Удаление документов

Как и другие, Flask-PyMongo предоставляет методы для удаления одной или нескольких записей с использованием delete_one() и delete_many() соответственно.

Аргументы этого метода такие же, как и у других методов. Давайте посмотрим на пример:

 @app.route("/delete_todo/<int:todoId>", methods=['DELETE']) 
 def delete_todo(todoId): 
 todo = db.todos.delete_one({'_id': todoId}) 
 return todo.raw_result 

Будет произведен поиск и удаление записи с предоставленным идентификатором. Если мы отправим на эту конечную точку запрос DELETE http://localhost:5000/delete_todo/5 , мы получим следующий результат:

 { 
 "n": 1, 
 "ok": 1.0 
 } 

В качестве альтернативы вы можете использовать метод find_one_and_delete() который удаляет и возвращает удаленный элемент, чтобы избежать использования неудобного объекта результата:

 @app.route("/delete_todo/<int:todoId>", methods=['DELETE']) 
 def delete_todo(todoId): 
 todo = db.todos.find_one_and_delete({'_id': todoId}) 
 if todo is not None: 
 return todo.raw_result 
 return "ID does not exist" 

Отправка http://localhost:5000/delete_todo/8 на наш сервер теперь приводит к:

 { 
 "_id": 8, 
 "body": "todo body two", 
 "title": "todo title two" 
 } 

Наконец, вы можете удалить все сразу с помощью delete_many() :

 @app.route('/delete_many', methods=['DELETE']) 
 def delete_many(): 
 todo = db.todos.delete_many({'title': 'todo title two'}) 
 return todo.raw_result 

Отправка http://localhost:5000/delete_many на наш сервер приведет к примерно следующему:

 { 
 "n": 1, 
 "ok": 1.0 
 } 

Сохранение и получение файлов

MongoDB позволяет нам сохранять двоичные данные в своей базе данных, используя спецификацию GridFS.

Flask-PyMongo предоставляет метод save_file() для сохранения файла в GridFS и метод send_file() для получения файлов из GridFS .

Начнем с маршрута загрузки файла в GridFS :

 @app.route("/save_file", methods=['POST', 'GET']) 
 def save_file(): 
 upload_form = """<h1>Save file</h1> 
 <form method="POST" enctype="multipart/form-data"> 
 <input type="file" name="file" id="file"> 
 <br><br> 
 <input type="submit"> 
 </form>""" 
 
 if request.method=='POST': 
 if 'file' in request.files: 
 file = request.files['file'] 
 mongodb_client.save_file(file.filename, file) 
 return {"file name": file.filename} 
 return upload_form 

В приведенном выше блоке кода мы создали форму для обработки загрузок и возврата имени файла загруженного документа.

Теперь давайте посмотрим, как получить только что загруженный файл:

 @app.route("/get_file/<filename>") 
 def get_file(filename): 
 return mongodb_client.send_file(filename) 

Этот блок кода вернет файл с указанным именем или выдаст ошибку 404, если файл не был найден.

Заключение

Расширение Flask-PyMongo предоставляет низкоуровневый API (очень похожий на официальный язык MongoDB) для связи с нашим экземпляром MongoDB.

Расширение также предоставляет несколько вспомогательных методов, поэтому нам не нужно писать слишком много шаблонного кода.

В этой статье мы увидели, как интегрировать MongoDB с нашим приложением Flask , мы также выполнили некоторые операции CRUD и увидели, как работать с файлами с MongoDB с помощью GridFS .

Я попытался осветить как можно больше, но если у вас есть какие-либо вопросы и / или предложения, пожалуйста, оставьте комментарий ниже.

comments powered by Disqus