学习Django中的模型

118 阅读8分钟

现在我们知道了Django中的路由,以及URL如何映射到视图和视图函数,将Html模板送回给用户,我们现在可以学习Django中的模型。 模型的概念是使用数据库中的动态数据来驱动用户所看到的和与之交互的网站前端。 例如,在我们的小博客应用程序中,我们想在数据库中存储一个帖子的集合。 Django中的模型是一个代表数据库中的表的类。 每种类型的数据,如帖子或用户,都由它自己的模型来表示。 一个模型映射到数据库中的一个表。


创建一个帖子模型

当我们运行python manage.py startapp posts命令时,创建的文件之一是models.py文件。 Django为我们创建了这个文件,所以我们可以在这个python文件中定义一个或多个Model。

django modelspy file

为了在这个文件中定义一个Model,我们可以添加一个Python类

posts/models.py

from django.db import models


# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)

Django为Model提供了许多字段类型。 在上面的代码中,我们使用了四种不同的字段类型。 我们有CharFieldSlugFieldTextFieldDateTimeField。 在模型中定义的每个字段都与一个给定的部件相关联,该部件将显示在管理区或网站的前端,用户可以与之互动。 CharField的默认表单部件是一个TextInput。 TextField的默认窗体部件是Textarea。 DateTimeField的默认表单部件是一个单一的TextInput。管理员使用两个独立的TextInput部件,并使用JavaScript快捷键。


Django迁移

在Django中使用Model的第一步是在models.py文件中创建Model类。 我们在上面的章节中采取了这一步骤。 迁移的步骤是将Model类映射到数据库中的一个表。 在我们创建并运行自己的迁移之前,我们需要看看Django已经创建的其他迁移。 你可能已经注意到,当你在终端运行python manage.py runserver命令时,会出现一条关于未应用的迁移的信息。

djangoblog $python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...

System check identified no issues (0 silenced).

You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.

Django version 3.0.3, using settings 'djangoblog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

因此,让我们继续前进,先通过运行python manage.py migrate命令来迁移内置的迁移程序。

djangoblog $python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying sessions.0001_initial... OK

对于你自己的模型,你首先需要创建一个或多个迁移文件。 迁移文件可以跟踪你对模型所做的任何修改。 所以当你对一个模型进行更新时,你需要同时更新迁移文件。 我们将在进一步的进展中看到这个动作,但现在让我们开始使用python manage.py makemigrations命令。

djangoblog $python manage.py makemigrations
Migrations for 'posts':
  postsmigrations001_initial.py
    - Create model Post

我们可以从输出中看到,Django识别出我们已经定义了一个Post Model。 Django读取了那个文件和它的Model定义,然后为我们创建了一个新的迁移文件,名为0001_initial.py。 非常酷!这意味着我们现在又有了一个新的迁移文件。

django model migration generation

这意味着我们现在又一次拥有了未应用的迁移。 事实上,如果我们再次运行服务器,我们会收到这样的信息:我们有一个未应用的迁移。

djangoblog $python manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).

You have 1 unapplied migration(s). Your project may not work properly until you
apply the migrations for app(s): posts.
Run 'python manage.py migrate' to apply them.

Django version 3.0.3, using settings 'djangoblog.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CTRL-BREAK.

我们现在可以退出服务器,然后像这样运行python manage.py migrate命令。

djangoblog $python manage.py migrate
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, posts, sessions
Running migrations:
  Applying posts.0001_initial... OK

很好!我们自己的自定义Post Model已经被迁移了。 我们自己的自定义Post Model现在已经被迁移了。 我们的Python类模型现在被映射到了数据库中的一个表中。 现在,任何时候我们做一个新的模型或者改变一个现有的模型,我们都需要经历同样的过程。 换句话说,我们需要先运行python manage.py makemigrations,然后再运行python manage.py migrate


Django ORM

Django ORM是与我们的数据库进行交互的一个好方法。 对象关系映射器是连接代码和数据库的桥梁。 ORM通过我们定义的模型提供了完整的创建、读取、更新和删除功能。 在我们将代码放到应用程序中之前,我们可以简单地从Django的交互式外壳开始使用。

Django的外壳

Python本身就有一个shell,你可以简单地输入python,然后点击Enter,得到一个提示,你可以输入Python代码来执行。 这不是那个shell,它是一个相当重要的区别。 如果你想在Django中使用shell,你需要确保在Django项目的根目录下使用python manage.py shell命令。 如果你试图使用普通的Python shell,在试图与你的Models进行交互时,你会遇到错误。 所以当你运行Django shell时,你会看到类似这样的东西。

djangoblog $python manage.py shell
Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:21:23) [MSC v.1916 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>>

要开始使用我们创建的Model,请输入以下内容。

>>> from posts.models import Post
>>> Post

首先,我们从models.py文件中导入Post模型,该文件存在于post应用程序中。 然后我们只需在命令行中输入Post,就可以看到它是Post模型类的一个实例。 由于这个类继承了Django中的models.Model,这意味着我们可以使用各种方法来处理数据库的问题。

插入一个帖子

数据库中还没有任何帖子,所以让我们继续使用ORM向数据库中输入一个新的帖子。 下面是如何做到这一点的。

>>> post = Post()
>>> post.title = 'A New Title!'
>>> post.save()

还有更多的字段需要填充,但我们只是想看看如何使用.save()方法来保存一些东西到数据库中。

抓取所有记录

现在数据库里有一个帖子,我们应该能够从终端查询数据库并看到结果。 现在让我们来试试。

>>> Post.objects.all()
]>

果然,我们看到QuerySet中有一个结果,那就是我们刚刚保存的那个帖子。

取回第一条记录

我们在数据库中只有一条记录,但要明确地获取第一条记录,我们可以用这个语法来做。

>>> Post.objects.all()[0]

正如你所看到的,这将返回一个帖子的实例,而不是一个QuerySet。 再进一步说,我们甚至可以用这个语法深入到表中的特定字段。

>>> Post.objects.all()[0].title
'A New Title!'

所以,这很好,我们已经创建了一个Model,将一个新的Model输入到数据库中,并从数据库中检索该Model,所有这些都使用Django ORM。 我们根本不需要手动编写任何SQL,Django为我们处理了所有这些。

定义一个__str__函数

为了使我们的Models更加人性化,我们可以添加一个新的函数,它将决定一个Post在管理区和shell中的样子。 所以这个函数不是作为一个对象而是作为实例的标题输出帖子的字符串版本。

from django.db import models


# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

让我们再一次尝试在交互式外壳中使用帖子模型。

>>> from posts.models import Post
>>> Post.objects.all()
]>

很整齐 现在,当我们使用*Post.objects.all()*获取所有的帖子时,我们在查询集中看到了标题。 如果我们在数据库里有更多的帖子,我们也会看到这些标题。 事实上,我们可以用不同的标题添加另一个帖子,然后执行同样的查询来查看结果。

>>> post = Post()
>>> post.title = 'Second Post!'
>>> post.save()
>>> Post.objects.all()
, ]>

使用管理员与模型进行交互

我们已经学会了如何配置Django的管理区,所以让我们继续在这个项目上启用它。 回顾一下这样做的步骤:首先用命令python manage.py createsuperuser 在 manage.py 文件中创建一个超级用户。

djangoblog $python manage.py createsuperuser
Username (leave blank to use 'myusername'): admin
Email address: admin@example.com
Password:
Password (again):
Error: Blank passwords aren't allowed.
Password:
Password (again):
Superuser created successfully.

一旦你创建了一个超级用户,继续在一个网络浏览器中访问http://127.0.0.1:8000/admin 地址。 这将转发到http://127.0.0.1:8000/admin/login/?next=/admin/,并提供我们在这里看到的登录选项。 不要忘记在创建超级用户后运行python manage.py runserver

django admin login

一旦你提供了你需要的凭证,你会看到你现在已经登录了。

django superuser logged in

让应用程序在管理中可见

当第一次登录Django的管理区时,你只会看到处理组和用户的能力。 为了告诉Django我们想让Posts应用程序显示在管理区,我们可以在应用程序的admin.py文件中注册我们的模型。 下面是如何做到这一点的。

from django.contrib import admin
from .models import Post

# Register your models here.
admin.site.register(Post)

上面突出显示的代码是我们需要手动添加到admin.py中的内容。 第一行强调的是导入Post Model,这样我们就可以使用它了。 然后在第二行,我们所要做的就是注册这个模型,这样它就会出现在管理界面中。 当然,我们可以再次访问管理界面,现在我们有能力与Post模型交互。

register model app for admin Django

我们还在Post模型中设置了 **__str__**方法在Post模型中。 这个方法改变了Post对象的字符串表示,使用Post的标题,而不是像Object这样的非描述性的默认值。 这就是为什么我们现在在管理界面上看到每个帖子的标题。

str in admin Django

现在,使用Django的管理界面与Post模型进行交互是很容易的。

post model django

为了好玩和练习,我们可以再回去测试一下使用Django ORM查询数据库的情况。

djangoblog $python manage.py shell
Python 3.8.0 (tags/v3.8.0:fa919fd, Oct 14 2019, 19:21:23) [MSC v.1916 32 bit (Intel)] on win32
Type "help", "copyright", "credits" or "license" for more information.
(InteractiveConsole)
>>> from posts.models import Post
>>> post1 = Post.objects.all()[0]
>>> post1.title
'A New Title!'
>>> post1.slug
'a-new-title'
>>> post1.body
'This is the first Post in the Django blog system.  We could have added the slug and body fields using the interactive shell, but this is an example of how to interact with your Model using the Django Admin Interface.'
>>> post2 = Post.objects.all()[1]
>>> post2.title
'Second Post!'
>>> post2.slug
'second-post'
>>> post2.body
'This is the second post of the Blog system in Django.  It was created using the Post model.  There are all kinds of things we could add to this post, but alas, here it is.'

获取所有模型并发送至模板

现在,如果我们访问帖子应用程序的基本URL(http://localhost:8000/posts/),我们仍然看到空的占位符Html。 这很好,我们已经正确地设置了URL路由,我们的视图也正确地渲染了一个模板来显示。 然而这只是静态数据。 由于我们的Post模型,我们现在在数据库中有了一些动态数据。 因此,让我们增加使用Django ORM获取所有记录的能力,然后将这些帖子传递给模板进行显示。 为了开始这个过程,我们需要编辑post应用程序中的views.py文件。

posts/views.py

from django.shortcuts import render
from .models import Post


def post_list(request):
    posts = Post.objects.all().order_by('date')
    return render(request, 'posts/post_list.html', {'posts': posts})

上面有三行突出显示的代码,让我们逐一解释每一行的作用。

  • 第一行突出显示的代码将Post模型导入该文件,这样我们就可以利用它了。
  • 第二行代码使用Django ORM来获取数据库中所有的Post记录,并按日期进行排序。 这确保了我们在显示帖子时的顺序。
  • 第三行突出显示的代码是为了强调我们如何在Django中将数据从视图传递到模板。 我们把字典作为render()函数的参数,就是我们把数据从视图传给模板的方式。

充分利用Django中的模板标签

现在我们正在向Django模板文件传递一些动态数据,我们可以利用Django中的模板标签来渲染模板文件中的这些动态数据。 回顾一下,Django中有两种类型的模板标签。 我们有 **{% %}**标签用于控制结构,而 **{{ }}**标签,用于在模板中输出数据。

posts/templates/posts/post_list.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Posts</title>
</head>
<body>
<h1>Posts List</h1>

{% for post in posts %}
    <h2>{{ post.title }}</h2>
    <h3>{{ post.body }}</h3>
    <h4>{{ post.slug }}</h4>
    <hr>
{% endfor %}

</body>
</html>

现在我们在[访问http://localhost:8000/posts/,而不是静态页面时看到了我们的帖子。

index page for posts app

添加一个自定义的模型方法

我们可以向Model类添加方法,以提供特定的功能。 其中一个例子可能是添加一个snippet()函数,只显示正文的前70个字符。 然后,在以后的时间里,我们可以添加一个详细信息页面,每个帖子都可以链接到该页面,以查看全文。

posts/models.py
我们可以像这样修改Post模型。

from django.db import models


# Create your models here.
class Post(models.Model):
    title = models.CharField(max_length=100)
    slug = models.SlugField()
    body = models.TextField()
    date = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

    def snippet(self):
        return self.body[0:70]

posts/templates/posts/post_list.html
然后在模板中,我们可以输出标题、片段和slug

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Posts</title>
</head>
<body>
<h1>Posts List</h1>

{% for post in posts %}
    <h2>{{ post.title }}</h2>
    <h3>{{ post.snippet }}...</h3>
    <h4>{{ post.slug }}</h4>
    <hr>
{% endfor %}

</body>
</html>

其结果是一个缩短的正文,只显示前70个字符。 相当酷啊!

django custom model method

Django中的模型总结

Django中的模型是关于你的应用程序中的数据的单一信息源。它包含了你所存储的数据的所有基本字段和相关行为。 一般来说,每个模型映射到一个数据库表。 每个模型都是一个Python类,其子类是django.db.models.Model。 模型的每个属性代表一个数据库字段。 一旦你正确地设置了你的模型,Django就会使用对象关系映射技术给你一个自动生成的数据库访问API。