介绍
在之前的教程“如何创建 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用于身份验证,以及reversefromdjango.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模型添加到此文件中。这涉及在其签名中添加另一个名为Commentwith 的类,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,则可以通过键入CTRLand 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。这些文件类似于commitsGit 等版本控制系统中的文件。
现在,如果您导航到~/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数据库表的过程。