如何创建Django模型

129 阅读9分钟

简介

在本教程中,我们将创建Django模型,定义我们将要存储的Blog应用数据的字段和行为。这些模型将数据从你的Django应用程序映射到数据库中。这是Django用来通过他们的对象关系映射(ORM)API生成数据库表的东西,被称为 "模型"。

先决条件

如果你没有跟上这个系列,我们做了以下假设。

  • 你已经安装了Django第4版或更高版本。
  • 你已经将你的Django应用连接到了一个数据库。
  • 你正在使用一个基于Unix的操作系统,最好是Ubuntu 22.04云服务器,因为这是我们测试过的系统。

由于本教程主要涉及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,同时还有一个注释,描述了要放到这个models.py 文件中的内容。

models.py

from django.db import models

# Create your models here.

使用你喜欢的文本编辑器,将以下代码添加到models.py 文件中。我们将使用nano 作为我们的文本编辑器,但欢迎你使用你喜欢的任何东西。

nano models.py

在这个文件中,已经添加了导入models API的代码,我们可以继续删除后面的注释。然后我们将导入slugify ,用于从字符串生成slugs,Django的User ,用于认证,以及从django.urls 导入reverse ,使我们在创建URL时更灵活。

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

然后,在我们将称为Post 的模型类上添加类方法,有以下数据库字段:title,slug,content,created_onauthor 。把这些添加到你的导入语句下面。

models.py

...
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的功能和保存帖子的功能。这很关键,因为这将创建一个独特的链接,以匹配我们独特的帖子。

models.py

...
    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 类通常包含其他与数据库字段定义无关的重要模型逻辑。

models.py

...
    class Meta:
        ordering = ['created_on']

        def __unicode__(self):
            return self.title

最后,我们将在这个文件中添加Comment 模型。这涉及到添加另一个名为Comment 的类,其签名中包含models.Models 并定义了以下数据库字段。

  • name - 发布评论的人的名字。
  • email - 发布评论的人的电子邮件地址。
  • text - 评论本身的文本。
  • post - 发表评论的帖子。
  • created_on - 评论创建的时间。

models.py

...
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 文件与以下内容一致。

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,你可以通过输入CTRLX ,然后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 部分,如下所示。

settings.py

# 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步 - 进行迁移

随着我们的模型PostComment 的添加,下一步是应用这些变化,以便我们的MySQL 数据库模式能够识别它们并创建必要的表。

首先,我们必须使用makemigrations 命令将我们的模型变化打包成单独的迁移文件。这些文件类似于Git等版本控制系统中的commits

现在,如果你导航到~/my_blog_app/blog/blogsite/migrations 并运行ls ,你会发现只有一个__init__.py 文件。一旦我们添加了迁移,情况就会改变。

使用cd ,改变到博客目录,像这样。

cd ~/my_blog_app/blog

然后在manage.py 上运行makemigrations 命令。

python manage.py makemigrations

然后,你应该在你的终端窗口中收到以下输出。

Output
Migrations 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.py0001_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

你会注意到,除了我们刚刚用模型PostComment 创建的0001_initial 的迁移之外,所有的迁移都被选中。

现在让我们检查一下,一旦我们进行了迁移,哪些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

我们将收到以下输出。

Output
Operations to perform:
  Apply all migrations: admin, auth, blogsite, contenttypes, sessions
Running migrations:
  Applying blogsite.0001_initial... OK

你现在已经成功地应用了你的迁移。

需要注意的是,正如Django文档中所说的那样,将MySQL作为后端进行Django迁移有三个注意事项。

  • 缺少对围绕模式改变操作的事务的支持。换句话说,如果一次迁移未能成功应用,你将不得不手动取消你所做的改变,以尝试另一次迁移。在迁移失败的情况下,不可能回滚到一个较早的时间点,也就是在进行任何改变之前。
  • 对于大多数模式改变的操作,MySQL将完全重写表。在最坏的情况下,时间的复杂性将与表中的行数成正比,以增加或删除列。根据Django文档,这可能会慢到每百万行1分钟。
  • 在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查询应该可以发现以下内容。

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_commentblogsite_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 +D 关闭MySQL,当你准备离开你的Python环境时,你可以运行deactivate 命令。

deactivate

停用你的编程环境将使你回到终端的命令提示符。

结论

在本教程中,我们已经成功地在一个博客网络应用中为基本功能添加了模型。你已经学会了如何编码modelsmigrations ,以及将Djangomodels 转化为实际的MySQL 数据库表的过程。