如何创建Django模型
引言
在之前的教程《如何创建一个Django应用并连接到数据库》中,我们介绍了如何创建一个MySQL数据库,如何创建和启动一个Django应用,并如何将其连接到一个MySQL数据库。
在本教程中,我们将创建Django模型来定义我们将存储的博客应用程序数据的字段和行为。这些模型将数据从您的Django应用程序映射到数据库。它是Django使用的通过对象关系映射(ORM)API生成数据库表的方式,被称为“模型”。
先决条件
这个教程是Django开发系列的一部分,也是这个系列的延续。
如果您还没有跟随本系列的进展,那我们做出以下假设:
- You have Django version 4 or higher installed.
- You have connected your Django app to a database. We are using MySQL, and you can achieve this connection by following part two of the Django series, “How To Create a Django App and Connect it to a Database.”
- You are working with a Unix-based operating system, preferably an Ubuntu 22.04 cloud server as this is the system we have tested on. If you would like to set up Django on a similar environment, please refer to our tutorial, “How To Install Django and Set Up a Development Environment on Ubuntu 22.04.”
由于本教程主要涉及Django模型,即使您的设置有些不同,您也可能能够跟随进行。
第一步 – 创建Django应用程序
为了与Django的模块化理念保持一致,我们将在项目中创建一个Django应用程序,其中包含创建博客网站所需的所有文件。
每当我们开始在Python和Django中工作时,我们应该激活Python虚拟环境并进入我们应用的根目录。如果您跟随整个系列操作,可以通过输入以下命令来实现这一点。
- cd ~/my_blog_app
- . env/bin/activate
- cd blog
从那里开始,让我们运行这个命令。
- python manage.py startapp 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
我们在本教程中要重点关注的文件是位于blogsite目录下的models.py文件。
第二步 – 添加文章模型 bù –
首先,我们需要打开并编辑models.py文件,以便其中包含用于生成帖子模型的代码。帖子模型包含以下数据库字段:
- title — The title of the blog post.
- slug — Where valid URLs are stored and generated for web pages.
- content — The textual content of the blog post.
- created_on — The date on which the post was created.
- author — The person who has written the post.
现在,进入包含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,包括以下数据库字段:标题、Slug、内容、创建时间和作者。在导入语句下方添加这些字段。
...
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 — The name of the person posting the comment.
- email — The email address of the person posting the comment.
- text — The text of the comment itself.
- post — The post with which the comment was made.
- created_on — The time the comment was created.
...
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,然后按下回车键来完成。
有了models.py文件的设置,我们可以继续更新我们的settings.py文件。
步骤3 — 更新设置
现在我们已经在我们的应用程序中添加了模型,我们必须告知我们的项目我们刚刚添加的博客应用程序的存在。我们通过将它添加到settings.py中的INSTALLED_APPS部分来实现这一点。
导航到包含你的settings.py文件的目录。
- cd ~/my_blog_app/blog/blog
从这里开始,打开您的settings.py文件,例如使用nano。
- nano settings.py
将文件打开后,按照下面所示,在文件的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',
]
通过添加博客应用程序,您可以保存并退出文件。
在此时,我们已经准备好开始应用这些变化。
第四步 – 进行迁移操作
在我们的模型中添加了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时,都会生成一个类似的文件。
如果您想查看该文件的内容,请在所在的目录中运行“0001_initial.py”少一些次数。
现在导航到~/my_blog_app/blog。
- cd ~/my_blog_app/blog
既然我们已经创建了迁移文件,我们必须使用迁移命令将这些文件所描述的修改应用到数据库中。但首先让我们使用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
你会注意到除了我们刚刚用模型Post和Comment创建的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迁移存在三个注意事项。
- Lack of support for transactions around schema alteration operations. In other words, if a migration fails to apply successfully, you will have to manually unpick the changes you’ve made in order to attempt another migration. It is not possible to rollback, to an earlier point, before any changes were made in the failed migration.
- For most schema alteration operations, MySQL will fully rewrite tables. In the worst case, the time complexity will be proportional to the number of rows in the table to add or remove columns. According to the Django documentation, this could be as slow as one minute per million rows.
- In MySQL, there are small limits on name lengths for columns, tables and indices. There is also a limit on the combined size of all columns and index covers. While some other backends can support higher limits created in Django, the same indices will fail to be created with a MySQL backend in place.
在考虑与Django一起使用的每个数据库时,一定要权衡每个数据库的优缺点。
第五步——验证数据库架构
完成迁移之后,我们应该验证通过 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模型迁移成功生成的。
当你想要关闭MySQL时,可以使用CTRL + D的组合键。当你准备离开Python环境时,可以运行deactivate命令。
- deactivate
禁用你的编程环境将使你返回到终端命令提示符。
结论 (jié
在这个教程中,我们在博客网站应用程序中成功添加了基本功能的模型。您已经学会如何编写模型,迁移工作的原理以及将Django模型转换为实际的MySQL数据库表的过程。