介绍
在之前的教程“如何创建 Django 应用程序并将其连接到数据库”中,我们介绍了如何创建 MySQL 数据库、如何创建和启动 Django 应用程序以及如何将其连接到 MySQL 数据库。
在本教程中,我们将创建 Django模型来定义我们将存储的博客应用程序数据的字段和行为。这些模型将数据从 Django 应用程序映射到数据库。这是 Django 用来通过其对象关系映射 (ORM) API(称为“模型”)生成数据库表的工具。
先决条件
本教程是Django 开发系列的一部分,是该系列的延续。
如果您还没有关注本系列,我们将做出以下假设:
- 您已安装 Django 版本 3 或更高版本。
- 您已将 Django 应用程序连接到数据库。我们正在使用 MySQL,您可以按照 Django 系列的第二部分“如何创建 Django 应用程序并将其连接到数据库”来实现这种连接。
- 您正在使用基于 Unix 的操作系统,最好是 Ubuntu 20.04 云服务器,因为这是我们测试过的系统。如果您想在类似的环境中设置 Django,请参阅我们的教程,“如何在 Ubuntu 20.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 步 – 添加 Posts 模型
首先,我们需要打开并编辑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,DjangoUser
用于身份验证,以及reverse
fromdjango.urls
为我们创建 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
with 的类,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
and X
,然后Y
,然后 来实现ENTER
。
随着models.py
文件设置,我们可以继续更新我们的settings.py
文件。
第 3 步 – 更新设置
现在我们已经向我们的应用程序添加了模型,我们必须通知我们的项目blogsite
我们刚刚添加的应用程序的存在。我们通过将它添加到 中的INSTALLED_APPS
部分来做到这一点settings.py
。
导航到您settings.py
居住的目录。
- cd ~/my_blog_app/blog/blog
从这里,打开您的settings.py
文件,例如使用 nano。
- 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
。这些文件类似于commits
Git 等版本控制系统中的文件。
现在,如果您导航到~/my_blog_app/blog/blogsite/migrations
并运行ls
,您会注意到只有一个__init__.py
文件。一旦我们添加了迁移,这种情况就会改变。
使用 切换到博客目录cd
,如下所示:
- cd ~/my_blog_app/blog
然后在makemigrations
上运行命令manage.py
。
- python manage.py makemigrations
然后,您应该在终端窗口中收到以下输出:
OutputMigrations for 'blogsite':
blogsite/migrations/0001_initial.py
- Create model Post
- Create model Comment
还记得,当我们导航到/~/my_blog_app/blog/blogsite/migrations
它时,它只有__init__.py
文件吗?如果我们现在cd
回到那个目录,我们会注意到添加了两个项目:__pycache__
和0001_initial.py
。该0001_initial.py
文件是在您运行时自动生成的makemigrations
。每次运行时都会生成一个类似的文件makemigrations
。
less 0001_initial.py
如果您想阅读文件包含的内容,请从它所在的目录运行。
现在导航到~/my_blog_app/blog
:
- cd ~/my_blog_app/blog
由于我们已经制作了一个迁移文件,我们必须使用命令将这些文件描述的更改应用于数据库migrate
。但首先让我们使用showmigrations
命令检查当前存在哪些迁移。
- python manage.py showmigrations
Outputadmin
[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
我们刚刚使用模型Post
和Comment
.
现在让SQL
我们使用以下命令检查迁移后将执行哪些语句。它接受迁移和迁移的标题作为参数:
- python manage.py sqlmigrate blogsite 0001_initial
下面揭示的是在幕后进行的实际 SQL 查询。
Output--
-- 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
我们将收到以下输出:
OutputOperations to perform:
Apply all migrations: admin, auth, blogsite, contenttypes, sessions
Running migrations:
Applying blogsite.0001_initial... OK
您现在已成功应用迁移。
重要的是要记住,如 Django 文档中所述,使用 MySQL 作为后端的 Django 迁移有 3 个警告。
- 缺乏对围绕模式更改操作的事务的支持。换句话说,如果迁移未能成功应用,您将必须手动取消您所做的更改以尝试另一次迁移。在失败的迁移中进行任何更改之前,不可能回滚到更早的点。
- 对于大多数模式更改操作,MySQL 将完全重写表。在最坏的情况下,时间复杂度将与表中要添加或删除列的行数成正比。根据 Django 文档,这可能慢到每百万行一分钟。
- 在 MySQL 中,列、表和索引的名称长度有很小的限制。所有列和索引覆盖的组合大小也有限制。虽然其他一些后端可以支持在 Django 中创建的更高限制,但在 MySQL 后端就位的情况下将无法创建相同的索引。
对于您考虑与 Django 一起使用的每个数据库,请务必权衡每个数据库的优缺点。
第 5 步 – 验证数据库架构
迁移完成后,我们应该验证我们通过 Django 模型创建的 MySQL 表是否成功生成。
为此,请在终端中运行以下命令以登录 MySQL。我们将使用djangouser
我们在上一教程中创建的。
- mysql blog_data -u djangouser
现在,选择我们的数据库blog_data
。如果您不知道正在使用的数据库,则可以SHOW DATABASES;
在 SQL 中显示所有数据库。
- USE blog_data;
然后键入以下命令以查看表。
- SHOW TABLES;
此 SQL 查询应显示以下内容:
Output+----------------------------+
| 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;
Output+------------+--------------+------+-----+---------+----------------+
| 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;
Output+------------+--------------+------+-----+---------+----------------+
| 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
+关闭 MySQL,D
当您准备好离开 Python 环境时,您可以运行以下deactivate
命令:
- deactivate
停用您的编程环境将使您回到终端命令提示符。
结论
在本教程中,我们成功地为博客 Web 应用程序中的基本功能添加了模型。您已经学习了如何编码models
、如何migrations
工作以及将 Django 转换models
为实际MySQL
数据库表的过程。