Вступление
Создание веб-приложения почти всегда означает работу с данными из базы данных. Существуют различные базы данных на выбор, в зависимости от ваших предпочтений.
В этой статье мы рассмотрим, как интегрировать одну из самых популярных баз данных 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 для:
- Создание данных
- Доступ к данным
- Изменение данных
Он не использует какую-либо предопределенную схему, поэтому может в полной мере использовать бессхемный характер 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()
имеет следующие аргументы:
filter
- запрос, который определяет, какие записи будут заменены.replacement
- записи, которые будут заменены при замене.{}
- объект конфигурации, у которого есть несколько параметров, из которых мы должны сосредоточиться -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 .
Я попытался осветить как можно больше, но если у вас есть какие-либо вопросы и / или предложения, пожалуйста, оставьте комментарий ниже.