简介
在上一篇教程“如何创建 Django 应用程序并将其连接到数据库”中,我们介绍了如何创建一个 MySQL 数据库,如何创建和启动一个 Django 应用程序,以及如何将其连接到一个 MySQL 数据库。
在本教程中,我们将创建 Django 模型,定义我们将要存储的博客应用程序数据的字段和行为。这些模型将 Django 应用程序的数据映射到数据库。这是 Django 用来通过对象关系映射(ORM)API生成数据库表的方式,被称为“模型”。
先决条件
本教程是 Django 开发系列的一部分,是该系列的延续。
如果您没有跟随这个系列,我们做出以下假设:
- 您已安装 Django 版本为 4 或更高版本。
- 您已将 Django 应用程序连接到数据库。我们正在使用 MySQL,您可以通过跟随 Django 系列的第二部分“如何创建 Django 应用程序并将其连接到数据库”来实现这种连接。
- 您正在使用基于 Unix 的操作系统,最好是 Ubuntu 22.04 云服务器,因为这是我们测试的系统。如果您想在类似的环境中设置 Django,请参考我们的教程“如何在 Ubuntu 22.04 上安装 Django 并设置开发环境”。
由于本教程主要涉及 Django 模型,即使您的设置有些不同,您也可以跟着做。
步骤 1 — 创建 Django 应用程序
为了与 Django 的模块化哲学保持一致,我们将在我们的项目中创建一个 Django 应用程序,其中包含创建博客网站所需的所有文件。
每当我们开始在 Python 和 Django 中工作时,我们应该激活我们的 Python 虚拟环境并进入我们的应用程序的根目录。如果您跟着这个系列,您可以通过输入以下内容来实现这一点。
cd ~/my_blog_app . env/bin/activate cd blog
然后,让我们运行以下命令:
python manage.py startapp blogsite
这将创建我们的应用程序以及一个 blogsite
目录。
在本教程系列的这一点上,您的项目将具有以下目录结构:
my_blog_app/ └── blog ├── blog │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-38.pyc │ │ ├── settings.cpython-38.pyc │ │ ├── urls.cpython-38.pyc │ │ └── wsgi.cpython-38.pyc │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── blogsite │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py └── manage.py
在本教程中,我们将专注于 models.py
文件,该文件位于 blogsite
目录中。
步骤 2 — 添加文章模型
首先,我们需要打开并编辑 models.py
文件,以便其中包含生成 Post
模型的代码。Post
模型包含以下数据库字段:
title
— 博客文章的标题。slug
— 存储和生成网页的有效 URL。content
— 博客文章的文本内容。created_on
— 文章创建的日期。author
— 撰写文章的人。
现在,进入包含 models.py
文件的目录。
cd ~/my_blog_app/blog/blogsite
使用 cat
命令在终端中显示文件的内容。
cat models.py
文件应该包含以下代码,其中导入了模型,并包含了一个描述应该放入这个 models.py
文件的注释。
from django.db import models # Create your models here.
使用您喜欢的文本编辑器,将以下代码添加到 models.py
文件中。我们将使用 nano
作为我们的文本编辑器,但您可以使用您喜欢的任何编辑器。
nano models.py
在这个文件中,已经添加了导入模型 API 的代码,我们可以继续删除后面的注释。然后,我们将导入 slugify
以从字符串生成 slug,Django 的 User
用于身份验证,以及从 django.urls
导入 reverse
以便更灵活地创建 URL。
from django.db import models from django.template.defaultfilters import slugify from django.contrib.auth.models import User from django.urls import reverse
然后,在我们将要称为 Post
的模型类上添加类方法,其中包含以下数据库字段:title
、slug
、content
、created_on
和 author
。将这些添加到您的导入语句下面。
... class Post(models.Model): title = models.CharField(max_length=255) slug = models.SlugField(unique=True, max_length=255) content = models.TextField() created_on = models.DateTimeField(auto_now_add=True) author = models.TextField()
接下来,我们将为生成 URL 添加功能,并为保存文章添加功能。这是至关重要的,因为这将创建一个唯一的链接来匹配我们的唯一文章。
... def get_absolute_url(self): return reverse('blog_post_detail', args=[self.slug]) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) super(Post, self).save(*args, **kwargs)
现在,我们需要告诉模型帖子应该如何排序,并在网页上显示。这个逻辑将被添加到一个嵌套的内部 Meta
类中。Meta
类通常包含与数据库字段定义无关的其他重要模型逻辑。
... class Meta: ordering = ['created_on'] def __unicode__(self): return self.title
最后,我们将在这个文件中添加 Comment
模型。这涉及添加另一个名为 Comment
的类,其签名中包含 models.Models
,并定义以下数据库字段:
name
— 发表评论的人的姓名。email
— 发表评论的人的电子邮件地址。text
— 评论本身的文本。post
— 发表评论的文章。created_on
— 评论创建的时间。
... class Comment(models.Model): name = models.CharField(max_length=42) email = models.EmailField(max_length=75) website = models.URLField(max_length=200, null=True, blank=True) content = models.TextField() post = models.ForeignKey(Post, on_delete=models.CASCADE) created_on = models.DateTimeField(auto_now_add=True)
到目前为止,models.py
将会完成。确保您的 models.py
文件与以下内容匹配:
from django.db import models from django.template.defaultfilters import slugify from django.contrib.auth.models import User from django.urls import reverse class Post(models.Model): title = models.CharField(max_length=255) slug = models.SlugField(unique=True, max_length=255) content = models.TextField() created_on = models.DateTimeField(auto_now_add=True) author = models.TextField() def get_absolute_url(self): return reverse('blog_post_detail', args=[self.slug]) def save(self, *args, **kwargs): if not self.slug: self.slug = slugify(self.title) super(Post, self).save(*args, **kwargs) class Meta: ordering = ['created_on'] def __unicode__(self): return self.title class Comment(models.Model): name = models.CharField(max_length=42) email = models.EmailField(max_length=75) website = models.URLField(max_length=200, null=True, blank=True) content = models.TextField() post = models.ForeignKey(Post, on_delete=models.CASCADE) created_on = models.DateTimeField(auto_now_add=True)
确保保存并关闭文件。如果您使用的是 nano,您可以通过输入 CTRL
和 X
,然后 Y
,然后 ENTER
来完成。
有了设置好的 models.py
文件,我们可以继续更新我们的 settings.py
文件。
步骤 3 — 更新设置
现在我们已经向我们的应用程序添加了模型,我们必须告知我们的项目刚刚添加的 blogsite
应用程序的存在。我们通过将其添加到 settings.py
中的 INSTALLED_APPS
部分来实现这一点。
导航到包含您的 settings.py
的目录。
cd ~/my_blog_app/blog/blog
然后,使用 nano 或其他编辑器打开您的 settings.py
文件。
nano settings.py
打开文件后,按照下面的指示将您的 blogsite
应用程序添加到文件的 INSTALLED_APPS
部分。
# Application definition INSTALLED_APPS = [ 'blogsite', 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
添加了 blogsite
应用程序后,您可以保存并退出文件。
此时,我们已经准备好继续应用这些更改。
步骤 4 — 进行迁移
添加了我们的 Post
和 Comment
模型后,下一步是应用这些更改,以便我们的 MySQL
数据库模式识别它们并创建必要的表。
首先,我们必须使用 makemigrations
命令将我们的模型更改打包成单独的迁移文件。这些文件类似于版本控制系统(如 Git)中的提交。
现在,如果您导航到 ~/my_blog_app/blog/blogsite/migrations
并运行 ls
命令,您会注意到只有一个 __init__.py
文件。一旦我们添加了迁移,这将会改变。
使用 cd
切换到博客目录,如下所示:
cd ~/my_blog_app/blog
然后在 manage.py
上运行 makemigrations
命令。
python manage.py makemigrations
然后您将在终端窗口中收到以下输出:
Migrations for 'blogsite': blogsite/migrations/0001_initial.py - Create model Post - Create model Comment
记住,当我们导航到 /~/my_blog_app/blog/blogsite/migrations
时,它只有 __init__.py
文件?如果我们现在切换回该目录,我们会注意到已经添加了两个项目:__pycache__
和 0001_initial.py
。当您运行 makemigrations
时,0001_initial.py
文件会自动生成。每次运行 makemigrations
时都会生成类似的文件。
如果您想阅读文件的内容,请从所在目录运行 less 0001_initial.py
。
现在导航到 ~/my_blog_app/blog
:
cd ~/my_blog_app/blog
由于我们已经创建了迁移文件,我们必须使用 migrate
命令将这些文件描述的更改应用到数据库。但首先,让我们使用 showmigrations
命令检查当前存在哪些迁移。
python manage.py showmigrations
admin [X] 0001_initial [X] 0002_logentry_remove_auto_add [X] 0003_logentry_add_action_flag_choices auth [X] 0001_initial [X] 0002_alter_permission_name_max_length [X] 0003_alter_user_email_max_length [X] 0004_alter_user_username_opts [X] 0005_alter_user_last_login_null [X] 0006_require_contenttypes_0002 [X] 0007_alter_validators_add_error_messages [X] 0008_alter_user_username_max_length [X] 0009_alter_user_last_name_max_length [X] 0010_alter_group_name_max_length [X] 0011_update_proxy_permissions blogsite [ ] 0001_initial contenttypes [X] 0001_initial [X] 0002_remove_content_type_name sessions [X] 0001_initial
您会注意到除了我们刚刚创建的 0001_initial
之外,所有迁移都已经被检查。
现在让我们使用以下命令检查一旦我们进行迁移将执行哪些 SQL
语句,它接受迁移和迁移的标题作为参数:
python manage.py sqlmigrate blogsite 0001_initial
下面显示了幕后实际执行的 SQL 查询。
-- -- Create model Post -- CREATE TABLE `blogsite_post` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `title` varchar(255) NOT NULL, `slug` varchar(255) NOT NULL UNIQUE, `content` longtext NOT NULL, `created_on` datetime(6) NOT NULL, `author` longtext NOT NULL); -- -- Create model Comment -- CREATE TABLE `blogsite_comment` (`id` integer AUTO_INCREMENT NOT NULL PRIMARY KEY, `name` varchar(42) NOT NULL, `email` varchar(75) NOT NULL, `website` varchar(200) NULL, `content` longtext NOT NULL, `created_on` datetime(6) NOT NULL, `post_id` integer NOT NULL); ALTER TABLE `blogsite_comment` ADD CONSTRAINT `blogsite_comment_post_id_de248bfe_fk_blogsite_post_id` FOREIGN KEY (`post_id`) REFERENCES `blogsite_post` (`id`);
现在让我们执行迁移,以便将它们应用到我们的 MySQL 数据库。
python manage.py migrate
您将收到以下输出:
Operations to perform: Apply all migrations: admin, auth, blogsite, contenttypes, sessions Running migrations: Applying blogsite.0001_initial... OK
您已成功应用了迁移。
请记住,根据 Django 文档,使用 MySQL 作为后端的 Django 迁移有三个注意事项。
- 不支持围绕模式更改操作的事务。换句话说,如果迁移未成功应用,您将不得不手动取消您所做的更改,以尝试另一个迁移。无法回滚到在失败迁移之前的任何更改。
- 对于大多数模式更改操作,MySQL 将完全重写表。在最坏的情况下,时间复杂度将与表中的行数成正比。根据 Django 文档,这可能慢到每百万行一分钟。
- 在 MySQL 中,列、表和索引的名称长度有限制。所有列和索引覆盖的组合大小也有限制。虽然其他一些后端可以支持 Django 中创建的更高限制,但在使用 MySQL 后端时,相同的索引将无法创建。
对于您考虑用于 Django 的每个数据库,务必权衡各自的优缺点。
步骤 5 —— 验证数据库模式
迁移完成后,我们应该验证通过 Django 模型创建的 MySQL 表的成功生成。
为此,请在终端中运行以下命令以登录到 MySQL。我们将使用之前教程中创建的 djangouser
。
mysql blog_data -u djangouser
现在,选择我们的数据库 blog_data
。如果你不知道正在使用的数据库,可以在 SQL 中使用SHOW DATABASES;
来显示所有数据库。
USE blog_data;
然后输入以下命令以查看表。
SHOW TABLES;
这个 SQL 查询应该会显示以下内容:
+----------------------------+ | Tables_in_blog_data | +----------------------------+ | auth_group | | auth_group_permissions | | auth_permission | | auth_user | | auth_user_groups | | auth_user_user_permissions | | blogsite_comment | | blogsite_post | | django_admin_log | | django_content_type | | django_migrations | | django_session | +----------------------------+ 12 rows in set (0.01 sec)
在这些表中包括 blogsite_comment
和 blogsite_post
。这些是我们刚刚创建的模型。让我们验证它们是否包含我们定义的字段。
DESCRIBE blogsite_comment;
+------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | name | varchar(42) | NO | | NULL | | | email | varchar(75) | NO | | NULL | | | website | varchar(200) | YES | | NULL | | | content | longtext | NO | | NULL | | | created_on | datetime(6) | NO | | NULL | | | post_id | int | NO | MUL | NULL | | +------------+--------------+------+-----+---------+----------------+ 7 rows in set (0.00 sec)
DESCRIBE blogsite_post;
+------------+--------------+------+-----+---------+----------------+ | Field | Type | Null | Key | Default | Extra | +------------+--------------+------+-----+---------+----------------+ | id | int | NO | PRI | NULL | auto_increment | | title | varchar(255) | NO | | NULL | | | slug | varchar(255) | NO | UNI | NULL | | | content | longtext | NO | | NULL | | | created_on | datetime(6) | NO | | NULL | | | author | longtext | NO | | NULL | | +------------+--------------+------+-----+---------+----------------+ 6 rows in set (0.00 sec)
我们已经验证了数据库表是否成功从我们的 Django 模型迁移中生成。
你可以使用 CTRL
+ D
退出 MySQL,当你准备离开 Python 环境时,可以运行 deactivate
命令:
deactivate
退出编程环境将使你回到终端命令提示符。
结论
在本教程中,我们成功为博客 Web 应用程序的基本功能添加了模型。你已经学会了如何编写 models
,migrations
的工作原理以及将 Django models
转换为实际的 MySQL
数据库表的过程。