如何将 Django 与 MongoDB 连接
最流行的 Web 框架之一 Django 采用了“内置电池”理念, 使用普通 Django 即可构建可以用于生产的应用程序,无需扩展程序或其他框架。 甚至数据库 SQLite 也是开箱即用。 SQLite 非常适合学习和开发小型应用程序,但也有一定局限,随着项目发展,这些局限往往会导致问题。
开发者倾向于选择 SQLite 之外的数据库,使应用可以扩缩和执行高负载操作。 Django 官方支持多种数据库,包括 PostgreSQL、MariaDB、MySQL、Oracle 和 SQLite,以及一些第三方数据库后端。 MongoDB 虽然并不在此列,但仍然是一个非常受欢迎的选择:根据 JetBrains 2023 年进行的 Django 开发者调查,有 8% 的 Django 开发者使用它。
谁需要它?为什么?
为 Django 项目选择 MongoDB 的可能原因是什么?Django 缺乏对此数据库的官方支持在实践中又意味着什么? 我们来看看。
所有官方支持的数据库都是关系数据库管理系统 (RDBMS),这意味着它们将数据存储在表中并支持 SQL(结构化查询语言)。 MongoDB 则是非关系数据库, 此类数据库也常被称为 NoSQL。 它们不使用具有行和列的表,而是以类似于 JSON 的格式 BSON 存储数据。
作为非关系数据库,MongoDB 相对于 SQL 数据库主要有以下优势:
- 无架构:如需存储结构可能随时间变化的复杂数据,BSON 的灵活性非常适用。
- 文档模型:数据格式可以因文档而异,是分层数据存储和多种非结构化数据的理想选择。
- 可扩缩性:MongoDB 在设计上可水平扩缩,支持分片处理大容量数据和高负载。
- 实时处理:借助 MongoDB,应用程序可以通过订阅更改流几乎立即对数据更改做出反应。
基于这些优势,开发者通常更喜欢将 MongoDB 用于处理用户生成内容的博客平台与应用程序、IoT(物联网)、大数据服务和移动应用程序。
不过,值得注意的是 MongoDB 不能作为 SQL 数据库的直接替代品。 由于 Django 没有对 MongoDB 的原生支持,您必须集成第三方软件包并相应地调整代码库。 此外,您必须组织整个开发流程,将以下限制纳入考量:
- Django 内置的面向 SQL 的 ORM 对于 MongoDB 失去作用。
- 您将无法再通过 Django 管理界面管理内容。
- 许多其他专为处理数据库而设计的 Django 功能将无法实现。 使用 MongoDB 时,您注定要脱离“Django 之道”。
已有的代码越多,需要的更改就越多。 因此,应该在项目生命周期的早期阶段切换到 MongoDB。
在本教程中,我们以两种方式将一个非常基本的 Django 项目与 MongoDB 连接:
- 使用 MongoEngine(基于 PyMongo)
- 仅使用 PyMongo
设备需求
开始前,您需要安装以下软件:
- PyCharm Professional 2023.3(提供 30 天免费试用)
- Python
- Docker(仅用于在容器中运行 MongoDB)
本教程将提供以下 Python 软件包和软件的安装说明:
- MongoDB Community(仅用于本地安装)
- Django
- PyMongo
- MongoEngine
准备 Django 项目
我们将使用带有基本 Django 待办事项列表应用程序的项目。 如果您想从头创建项目,请参阅 PyCharm 文档中的这篇教程。 或者直接克隆仓库,如下所示:
- 启动 PyCharm,然后在欢迎屏幕上点击 Get from VCS(从 VCS 获取)。
- 插入仓库 URL,点击 Clone(克隆)。
PyCharm 将下载项目并配置系统解释器。 在本教程中,我们将使用本地 virtualenv 解释器(也需要设置)。
在克隆项目后立即出现的弹出窗口中点击 Configure a Python interpreter(配置 Python 解释器)。 或者,点击右下角的解释器选择器,然后选择 Add New Interpreter | Add Local Interpreter(添加新解释器 | 添加本地解释器)。 有关在 PyCharm 中配置解释器的详细信息,请参阅文档。
配置解释器后,打开 Python Packages(Python 软件包)工具窗口并安装 Django。
安装 MongoDB
Django 项目已经就绪,接下来需要配置 MongoDB 数据库。 您有三种选择,具体取决于您的项目:
设置 MongoDB Atlas
如果要将 Django 项目与云 MongoDB 数据库连接,请注册 MongoDB Atlas 并部署免费的数据库集群。 要从应用程序访问此集群,您还需要将连接 IP 地址添加到 IP 访问列表并创建数据库用户。 对于本教程的后续步骤,您将需要连接字符串。 获取方法如下:
- 选择您的数据库。
- 点击 Overview(概览)标签页上的 Connect(连接)。
- 选择 Drivers(驱动程序)。
如需更多详细信息,请参阅 MongoDB Atlas 文档。
在 Docker 容器中运行 MongoDB
如果您决定在容器中运行 MongoDB Community Edition,请遵循以下步骤:
- 拉取 MongoDB Docker 镜像:
docker pull mongodb/mongodb-community-server
- 将镜像作为容器运行:
docker run --name mongo -d -p 27017:27017 mongodb/mongodb-community-server:latest
有关更多详情,请参阅 MongoDB 文档。
本地安装 MongoDB
如果您选择将 MongoDB 作为本地数据库运行,请执行以下步骤:
- 安装 MongoDB Community Edition。
- 启动 MongoDB Shell 并创建数据库:
% mongosh > use djangoTutorial
配置数据源
这一步非必选。 配置数据源将让您可以直接在 PyCharm 中查看数据库集合并跟踪其更改,无需安装额外软件或打开 MongoDB Atlas 的 Web 界面。
返回 PyCharm,打开 Database(数据库)工具窗口,点击“+”开始创建数据源。 选择 MongoDB 作为数据源类型。
配置新创建的数据源。 如果使用的是 MongoDB Atlas,首先将连接字符串插入 URL 字段,然后提供数据库用户的凭据。
如果您使用的是 MongoDB Community,无论是本地安装还是在 Docker 容器中运行,都请按如下方式配置数据源:
- 如果在安装过程中更改了默认设置,请输入数据库主机地址和端口号。 否则,请继续到第 2 步。
- 如果您已经配置用户名和密码,请在相应字段中输入。 否则,在 Authentication(验证)中选择 No auth(无身份验证)。
- 在 Database(数据库)字段中指定数据库名称。
- 点击 Test Connection(测试连接)。
如需详细了解如何在 PyCharm 中配置 MongoDB 数据源,请参阅文档。
配置环境变量
如果您使用的是 MongoDB Atlas 或采用用户名和密码身份验证的本地 MongoDB 数据库,则需要使用环境变量创建 .env 文件来存储凭据。 对项目进行版本控制时,这被视为防止机密数据泄露的最佳做法。
- 右键点击项目的根目录,然后从上下文菜单中选择 New | File(新建 | 文件)。
- 指定 .env 作为文件名。
- 新创建的文件将在编辑器中打开。 添加环境变量及其值。 例如:
USERNAME=jetbrains PASSWORD=czUfHKhGNxtGTsv HOST=cluster0.bqnh3eo.mongodb.net
仅 MongoDB Atlas 需要 HOST
。
- 右键点击编辑器中的任意位置,然后从上下文菜单中选择 Git | Add to .gitignore(Git | 添加到 .gitignore)。
接下来,您需要配置运行配置,以便在每次启动 Django 服务器时都加载新创建的 .env 文件。
- 点击上方 Run(运行)微件,从菜单中选择 Edit Configurations(编辑配置)。
- 在 Paths to “.env” files(“.env” 文件的路径)字段中指定 .env 的路径。
- 准备好后,点击 OK(确定)或 Run(运行)。
现在,您可以使用所选方法来连接 Django 和 MongoDB(或两种都尝试)。
使用 MongoEngine 连接 Django 和 MongoDB
MongoEngine 是用于 Django 项目的 Python 对象文档映射器 (ODM)。 它的运作方式类似于关系数据库中的 ORM(对象关系映射)。 以下是通过 MongoEngine 将 MongoDB 连接到 Django 的主要优点:
- ODM 允许使用常用语法在代码中定义文档架构(类似于将 Django 与 SQL 数据库一起使用时的模型)。
- 用于数据库查询的富 Python 式语法。
- 对 Django 表单的支持和 Django Rest Framework 序列化器。
综上所述,MongoEngine 在 Django 项目中提供了与 MongoDB 交互的高级体验。 如需将现有 Django 项目转换为使用 MongoDB 的项目,这也是理想选择。
安装并启用 MongoEngine
首先,在 Python Packages(Python 软件包)工具窗口中安装 MongoEngine 软件包。 打开工具窗口,输入 mongoengine,点击 Install(安装),然后选择版本。
Django 项目默认使用 SQLite 数据库创建。 我们需要通过注释(或移除)settings.py 中的 DATABASES 部分将其禁用。 按 ⌥⌘O / Ctrl+Alt+Shift+N,输入 DA… 以快速访问。
将以下内容添加到 settings.py 中:
import mongoengine mongoengine.connect(db="djangoTutorial", host="mongodb://localhost:27017/")
对于 MongoDB Atlas,从 .env 文件加载用户名和密码。 例如:
import os import mongoengine USERNAME = os.getenv("USERNAME") PASSWORD = os.getenv("PASSWORD") HOST = os.getenv("HOST") mongoengine.connect(db="djangoTutorial", host=f"mongodb+srv://{HOST}/", username=USERNAME, password=PASSWORD)
编辑模型
虽然 MongoDB 无架构,但 MongoEngine 允许在应用程序级别定义架构。 此类架构不会传递到数据库,但能够为 Django 和 MongoDB 之间的交互添加额外一层抽象,让开发者编写出更简洁且更易维护的代码。
与 Django ORM 不同,MongoEngine 使用文档而不是模型。 可惜,Django 管理界面不支持 MongoEngine 文档。 为了避免启动 Django 服务器时出现错误,请通过注释或移除相应的类在 admin.py 中取消注册 ToDoItem
。
您可以使用 Django Structure(Django 结构) 工具窗口快速访问模型管理类:
双击 Django Structure (Django 结构)工具窗口中 ToDoItem 下的 Models(模型),在编辑器中打开模型。 进行以下替换:
原始 | 替换 |
---|---|
from django.db import models |
from mongoengine import Document, fields 或
|
models.Model |
文档 |
models.CharField |
fields.StringField() |
models.DateField |
fields.DateField |
应该得到:
from mongoengine import Document, fields from django.utils import timezone class ToDoItem(Document): text = fields.StringField(max_length=100) due_date = fields.DateField(default=timezone.now) def __str__(self): return f"{self.text}: due {self.due_date}"
要避免手动替换,您可以让 Pycharm 的 AI Assistant 为您执行操作:
- 在 models.py 中选择代码。
- 从上下文菜单中选择 AI Actions | New Chat Using Selection(AI 操作 | 使用选区新建聊天)。
- 编写提示词,例如 Modify the code for usage with MongoEngine。
有关使用 AI Assistant 的信息,请参阅 PyCharm 文档。
为了向用户提供高水准、多元化、本土化的 AI Assistant 服务,JetBrains 目前正在与阿里云通义大模型商洽未来的合作事宜,为中国开发者带来 AI Assistant 服务。
*由于服务提供商的限制,一些地区的用户目前无法访问 AI Assistant。
现在,创建 ToDoItem
文档的实例并将其保存到数据库来测试解决方案。 打开 Python 控制台并运行以下代码:
from todo.models import ToDoItem from datetime import datetime task = ToDoItem(text="Fix the table") task.due_date = datetime(2023,11,29) task.save()
如果您使用的是云数据库,这可能需要一段时间。 如果出现 [SSL: CERTIFICATE_VERIFY_FAILED]
错误,请参阅故障排除建议。
在 Database(数据库)工具窗口中打开 to_do_item 集合并确保已添加记录:
确保更改不会破坏应用程序。 启动由 PyCharm 自动创建的 Django 服务器配置。
然后,在浏览器中转到 http://localhost:8000/todo/:
恭喜! 您的 Django 应用已经在使用 MongoDB 了!
使用 PyMongo 连接 Django 和 MongoDB
PyMongo 是官方推荐的低级 MongoDB 驱动程序, 在 Django 与 MongoDB 之间提供了直接而详细的交互途径。 如需编写数据库查询以获得更好的性能,并且在代码库中不需要类似 ORM 的体验,那么 PyMongo 是一个理想选择。
安装 PyMongo 并移除模型
MongoEngine 在后台运行 PyMongo。 因此,如果您刚刚完成本教程的前一部分,那么您的项目环境中已经安装了 pymongo
。 否则,在 Python Packages(Python 软件包)工具窗口中安装。
注释 settings.py 中的 DATABASES
部分禁用 SQLite,并从 admin.py 中移除 ToDoItemAdmin
(如果尚未移除)。
最后,从 models.py 中移除 ToDoItem
。 您可以使用 Recent Files(最近的文件)(⌘E / Ctrl+E) 快速访问。
转换视图
由于不再使用 Django 模型,我们应该适当更改视图类。 转到 views.py,添加一个将用于访问 MongoDB 数据库的函数:
from pymongo import MongoClient def get_db_handle(): client = MongoClient(host="mongodb://localhost", port=27017) db_handle = client.djangoTutorial.to_do_item return db_handle
对于 MongoDB Atlas 或采用身份验证的本地数据库,需要加载环境变量并使用连接字符串。 例如:
from pymongo import MongoClient import os USERNAME = os.getenv("USERNAME") PASSWORD = os.getenv("PASSWORD") HOST = os.getenv("HOST") def get_db_handle(): client = MongoClient(f"mongodb+srv://{USERNAME}:{PASSWORD}@{HOST}/?retryWrites=true&w=majority") db_handle = client.djangoTutorial.to_do_item return db_handle
函数返回 to_do_item
MongoDB 集合。 MongoDB 的优点之一是,即使这个集合还不存在,它也会在第一次插入时创建。
由于我们不再拥有 ToDoItem
模型,需要更新视图类 AllToDos
和 TodayToDos
的 get_queryset
方法:
class AllToDos(ListView): template_name = "todo/index.html" def get_queryset(self): db = get_db_handle() results = db.find() return results class TodayToDos(ListView): template_name = "todo/today.html" def get_queryset(self): db = get_db_handle() today = datetime.combine(date.today(), time()) results = db.find({"due_date": today}).sort("due_date") return results
这样更改代码,我们会重写 Django 的 ListView
类的默认 get_queryset
方法。 这段代码的作用如下:
AllToDos
类:
- 调用
get_db_handle
从djangoTutorial
数据库获取to_do_item
集合并将其指定给db
(第 5 行)。 - 应用 PyMongo 集合的
find()
方法获取集合中的所有条目(第 6 行)。
TodayToDos
类:
- 将当前日期与默认时间(午夜)组合设置为
today
。 我们的应用程序不需要时间,但不能去掉它,因为 BSON 格式需要(第 15 行)。 - 获取
due_date
为 today 的所有条目,并按due_date
排序(第 16 行)。
不要忘记更新导入。 将鼠标悬停在 datetime
和 time()
上(应以红色波浪线高亮显示),然后选择 Import this name(导入此名称)。 在这两个位置都从 datetime
导入。
如果 Django 服务器未运行,则将其启动,然后在浏览器中转到 http://localhost:8000/todo/ 以确保应用程序正常运行。
继续开发
目前,我们可以通过 Python 控制台将记录添加到数据库中。 但是,应用程序的用户无法使用控制台。 看起来应该开发一个视图,允许在浏览器中向数据库添加新记录。
首先编写一个 Web 表单。 在应用程序目录 (todo) 下创建一个 forms.py 文件,并填充以下代码:
from django import forms from datetime import datetime class ToDoItemForm(forms.Form): text = forms.CharField(max_length=100) due_date = forms.DateField(initial=datetime.now)
表单由两个字段组成:text
和 due_date
。 后者默认为当前日期。
现在,我们向 views.py 添加一个视图。 如本教程开头所述,如果您决定将 Django 与 MongoDB 一起使用,许多 Django 快捷键将不再有效。 您必须使用基于函数的视图并手动定义一些基本逻辑(如果使用不同的数据库,则可以通过基于类的视图直接继承)。
如果您使用 PyMongo 将 Django 连接到 MongoDB,函数代码应如下所示:
from django.shortcuts import redirect, render from .forms import ToDoItemForm ... def add_todo(request): if request.method == "POST": form = ToDoItemForm(request.POST) if form.is_valid(): new_todo = { "text": form.cleaned_data["text"], "due_date": datetime.combine(form.cleaned_data["due_date"], time()) } db = get_db_handle() db.insert_one(new_todo) return redirect('today') else: form = ToDoItemForm() return render(request, "todo/add.html", {"form": form})
详细情况如下:
- 如果
add_todo
视图收到 POST 请求,它会将ToDoItemForm
数据指定给form
(第 6-7 行)。 - 它首先确保发布的表单数据有效,然后根据表单数据创建字典(第 8-12 行)。
- 它通过
get_db_handle
方法连接到 MongoDB,并将新记录插入数据库(第 13-14 行)。 - 如果不是 POST 请求,它会返回一个空白表单(第 16 行)。
如果使用的是 MongoEngine,代码会更短,因为您可以使用 MongoEngine 文档中的内置 save()
方法:
from django.shortcuts import redirect, render from .forms import ToDoItemForm ... def add_todo(request): if request.method == "POST": form = ToDoItemForm(request.POST) if form.is_valid(): new_todo = ToDoItem(text=form.cleaned_data["text"], due_date=form.cleaned_data["due_date"]) new_todo.save() return redirect("today") else: form = ToDoItemForm() return render(request, "todo/add.html", {"form": form})
return 语句中对模板文件 add.html 的引用应使用黄色波浪线高亮显示。 将光标悬停在它上面并选择 Create template todo/add.html(创建模板 todo/add.html)。
模板将在编辑器中打开。 使用以下 HTML 代码填充:
{% extends "base.html" %} {% block content %} <h1>Add new to-do:</h1> <form method="post"> {% csrf_token %} {{ form.text.label_tag }} {{ form.text }} <br> {{ form.due_date.label_tag }} {{ form.due_date }} <br> <button type="submit">Submit</button> </form> {% endblock %}
通过添加用于创建新任务的按钮来更新 base.html:
<div> <a class="button" href='{% url "index" %}'>All tasks</a> <a class="button" href='{% url "today" %}'>Today</a> <a class="button" href='{% url "add" %}'>Add</a> </div>
更新 todo/urls.py 以允许您自己访问用于在浏览器中添加任务的页面:
urlpatterns = [ path("", views.AllToDos.as_view(), name="index"), path("today/", views.TodayToDos.as_view(), name="today"), path("add/", views.add_todo, name="add") ]
最后,该在浏览器中测试新功能了! 转到 http://localhost:8000/todo/add/,添加任务:
结论
如果您已完成本教程中的所有步骤,那么您已经通过 PyMongo 和 MongoEngine 这两个库成功将基本 Django 项目与 MongoDB 连接。
MongoEngine 实际上基于 PyMongo。 那么,它们的区别是什么?又如何为 Django 项目选择正确的方式呢? 我们在下表中总结了每种方法的优点和缺点:
MongoEngine | PyMongo | |
---|---|---|
访问集合 | Document 类映射到集合 |
通过 MongoClient 访问集合 |
查询 | 链式查询:ToDoItem.objects.filter(due_date=date.today()) |
字典式查询:db.find({"due_date": today}).sort("due_date") |
性能 | 由于对象文档映射,速度稍慢 | 使用 Python 字典作为数据模型,速度更快 |
使用复杂度 | 通过添加额外一层抽象和提供类似 ORM 的体验来简化使用 | 需要更深入地理解 MondoDB 的内部格式 |
常见问题解答
什么是 Django 和 MongoDB?
Django 和 MongoDB 都用于 Web 开发,但目的不同。 Django 是高级 Python Web 框架,遵循 MVT(模型-视图-模板)架构模式和 DRY(“避免重复代码”)原则。
MongoDB 适合 Django 吗?
Django 针对 SQL 数据库进行了优化。 MongoDB 是 NoSQL 数据库,虽然可以通过 MongoEngine 或 PyMongo 等工具集成,但这会增加复杂度并限制一些原生 Django 功能。 另一方面,MongoDB 有其优势,例如灵活的数据结构和良好的水平可扩缩性。 为 Django 选择 MongoDB 时,应考虑您的具体项目需求以及管理潜在额外开发挑战的准备程度。
可以将 NoSQL 与 Django 一起使用吗?
是的,您可以将 MongoDB 等 NoSQL 数据库与 Django 结合使用。 但是,这会增加复杂度,并可能限制一些针对 SQL 数据库优化的 Django 功能。 选择应基于您的具体项目要求和权衡。
PyMongo 和 MongoEngine 哪个更好?
PyMongo 速度更快,提供了对 MongoDB 的更直接访问,但也需要对 MongoDB 有更深的理解。 MongoEngine 提供了更高级别的接口,非常适合 Django,但可能不支持所有 MongoDB 功能,并且由于抽象,速度可能较慢。
为什么选择 MongoDB 而不是 SQL 配合 Django?
MongoDB 因其架构灵活性、大容量数据处理能力以及嵌套对象等独特功能,非常适合某些 Django 项目。 它可以跨多个服务器有效扩缩。 但是,使用它可能会限制一些针对 SQL 数据库优化的 Django 内置功能。
实用链接
文档和演示
教程
视频
播客
其他
本博文英文原作者: