本教程内容已过时,更新版教程请访问: Django 博客开发入门教程。
这是 Django 博客教程的第 4 篇,在阅读此篇教程以前,请确保你已阅读 Django 博客教程的前 3 篇:
1. Django 博客教程:前言
2. 搭建开发环境
3. 建立我们的 django 博客应用
编写博客的数据库模型
博客最主要的功能就是展示我们写的文章,它需要从某个地方获取我们写的博客文章数据才能把它展示出来,通常来说这个地方就是数据库。我们把写好的文章永久地保存在数据库里,当用户访问我们的博客时,django 就去数据库里把这些数据取出来展现给用户。
博客的文章应该含有标题、正文、作者、发表时间等数据,一个更加现代化的博客文章我们也希望它有分类、标签、评论等。为了更好地存储这些数据,我们需要合理地组织我们数据库的结构。
我们的博客初级版本主要包含这些数据:博客文章,文章会有分类以及标签。一篇文章只能有一个分类,但可以打上很多标签。数据库存储的数据其实就是表格的形式,例如存储我们的博客文章的数据库表长这个样子:
文章 id | 标题 | 正文 | 发表时间 | 分类 | 标签 |
---|---|---|---|---|---|
1 | title1 | text1 | 2016-12-23 | django | django 学习 |
2 | title2 | text2 | 2016-12-24 | django | django 学习 |
3 | title3 | text3 | 2016-12-26 | Python | Python 学习 |
其中 id 是一个数字,唯一对应着一篇文章。当然还可以在列上加入更多的信息,这只是一个最基本的示例。
数据库表设计成这样其实已经可以了,但是稍微分析一下我们就会发现一个问题,这 3 篇文章的分类和标签都是相同的,这会产生很多重复数据,当数据量很大时就浪费了存储空间。不同的文章可能它们的分类或者标签是相同的,所以我们把分类和标签拿出来,做成单独的数据库表,再把文章和分类与标签关联起来就可以了。下面分别是分类和标签的数据库表:
分类 id | 分类名 |
---|---|
1 | django |
2 | Python |
标签 id | 标签名 |
---|---|
1 | django 学习 |
2 | Python 学习 |
以上是自然语言描述的表格,数据库也和编程语言一样,有它自己的一套规定的语法来生成上述的表格结构,这样我们才能把数据存进去。一般情况下这时候我们应该先去学习数据库创建表格的语法,再回来写我们的博客程序了。但是 django 跟我们说不用这么麻烦,我已经帮你做了一些事情,我把那一套数据库的语法转换成了 Python 的语法形式,你写你的 Python 代码就可以了,翻译的工作我来帮你。用更加专业一点的说法,就是 django 为我们提供了一套 ORM(Object Relational Mapping)系统。比如说我们的分类数据库表,django 只要求我们这样写:
blog/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
这就是一个标准的 Python 的类,我们继承了 models.Model
类,类名为 Category(分类),Category 类有一个属性 name
,它是 models.CharField
的一个实例。这样,django 就可以把这个类翻译成数据库的语法,在数据库里创建一个名为 category 的表格,这个表格的一个列名为 name(即分类名),还有一个列 id 则会自动帮我们创建。其规则就是一个 Python 类对应一个数据库表格,类名即表名,类的属性对应着表格的列,属性名即列名。我们需要 3 个表格:文章(Post)、分类(Category)以及标签(Tag),下面就来创建它们。已经在代码中做了详细的注释,说明每一句代码的含义。但如果你在移动端下阅读不便的话,也可以跳到代码后面看正文的里的讲解。
blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
"""
django 要求我们必须继承 models.Model 类,
Category 只需要一个简单的分类名 name 就可以了。
CharField 指定了 name 的数据类型,
CharField 是字符型,
max_length 指定其最大长度,
超过这个长度的分类名就不能被存入数据库。
当然 django 还为我们提供了各种各样的类型,
如日期时间类型 DateTimeField、
整数类型 IntegerField 等等。
django 内置的类型全部类型可查看文档:
https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types
"""
name = models.CharField(max_length=100)
class Tag(models.Model):
"""
标签 Tag 也比较简单,
和 Category 一样。
再次强调一定要继承 models.Model 类!
"""
name = models.CharField(max_length=100)
class Post(models.Model):
"""
文章的数据库表稍微复杂一点,主要是涉及的字段更多。
"""
# 文章标题
title = models.CharField(max_length=70)
# 文章正文,我们使用了 TextField。
# 比较短的字符串存储可以使用 CharField,
# 但对于文章的正文来说可能会是一大段文本,
# 因此使用 TextField 来存储大段文本。
body = models.TextField()
# 这两个列分表表示了文章的创建时间和最后一次修改时间,
# 存储时间的列用 DateTimeField。
created_time = models.DateTimeField()
modified_time = models.DateTimeField()
# 文章摘要,可以没有文章摘要,
# 但默认情况下 CharField 要求我们必须存入数据,
# 否则就会报错。
# 指定 blank=True 后就可以允许空值了。
excerpt = models.CharField(max_length=200, blank=True)
# 这是分类与标签,
# 分类与标签的模型我们已经定义在上面。
# 我们在这里把文章对应的数据库表和分类与标签对应的表关联起来,
# 但是关联形式稍微有点不同。
# 我们规定一篇文章只能对应一个分类,
# 但是一个分类下可以有很多篇文章,
# 所以我们使用的是 ForeignKey,
# 即一对多的关系。
# 而对于标签来说,
# 一篇文章可以有多个标签,
# 同一个标签下也可能有多篇文章,
# 所以我们使用 ManyToManyField,
# 表明这是多对多的关系。
# 同时我们规定文章可以没有标签,
# 因此为标签 tags 指定了 blank=True。
# 如果你对 ForeignKey、ManyToManyField 不了解,
# 请看教程中的解释,
# 亦可参考官方文档:
# https://docs.djangoproject.com/en/1.10/topics/db/models/#relationships
category = models.ForeignKey(Category)
tags = models.ManyToManyField(Tag, blank=True)
# 文章作者
# 这里 User 是从 django.contrib.auth.models 导入的。
# django.contrib.auth 是 django 内置的应用,
# 专门用于处理网站用户的注册、登录等流程,
# User 是 django 为我们已经写好的用户模型,
# 这里我们通过 ForeignKey 把文章和 User 关联起来,
# 因为我们规定一篇文章只能有一个作者,
# 而一个作者可能会写多篇文章,
# 因此这是一对多的关系,
# 和 Category 类似。
author = models.ForeignKey(User)
数据库模型详解
首先是 Category(分类)和 Tag(标签)类,它们均继承自 model.Model
类,这是 django 规定的。它们均有一个 name 属性,用来存储它们的名称。由于分类名和标签名一般都是用字符串表示,因此我们使用了 CharField
来指定 name
的数据类型,同时 max_length
参数则指定 name
的最大长度。除了 CharField
,django 还为我们提供了更多内置的数据类型,比如时间类型 DateTimeField
、整数类型 IntegerField
等等。在本教程中我们会教你这些类型的使用方法,但以后你开发自己的项目时,你就需要通过阅读 [django 的官方文档关于字段类型的介绍][1]来了解有哪些数据类型以及如何使用它们。
[1]: https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types
Post(文章)类也一样,必须继承自 model.Model
类。文章的数据库表稍微复杂一点,主要是列更多。我们为它指定了这些列:
title。这是文章的标题,数据类型是 CharField,最大长度 max_length = 70。
body。文章正文,我们使用了 TextField。比较短的字符串存储可以使用 CharField,但对于文章的正文来说可能会是一大段文本,因此使用 TextField 来存储大段文本。
created_time、modified_time。这两个列分表表示了文章的创建时间和最后一次修改时间,存储时间的列用 DateTimeField 数据类型
excerpt。文章摘要,可以没有文章摘要,但默认情况下 CharField 要求我们必须存入数据,否则就会报错。指定 blank=True 后就可以允许空值了。
category 和 tags。这是分类与标签,分类与标签的模型我们已经定义在上面。我们在这里把文章对应的数据库表和分类与标签对应的表关联起来,但是关联形式稍微有点不同。我们规定一篇文章只能对应一个分类,但是一个分类下可以有很多篇文章,所以我们使用的是 ForeignKey,即一对多的关系。而对于标签来说,一篇文章可以有多个标签,同一个标签下也可能有多篇文章,所以我们使用 ManyToManyField,表明这是多对多的关系。同时我们规定文章可以没有标签,因此为标签 tags 指定了 blank=True。
author。文章作者,这里 User 是从 django.contrib.auth.models 导入的。django.contrib.auth 是 django 内置的应用,专门用于处理网站用户的注册、登录等流程,User 是 django 为我们已经写好的用户模型,这里我们通过 ForeignKey 把文章和 User 关联起来,因为我们规定一篇文章只能有一个作者,而一个作者可能会写多篇文章,因此这是一对多的关系,和 Category 类似。
注:这里我们使用了两种关联数据库表的形式,一种是 ForeignKey,它表明一种一对多的关联。比如这里我们的文章和分类的关系,一篇文章只能对应一个分类,而一个分类下可以有多篇文章。另外一中是 ManyToManyField,看名字就知道这是一种多对多的关联关系,比如这里的文章和标签,一篇文章可以有多个标签,而一个标签下也可以有多篇文章。假如你对此有一些困惑,强烈建议阅读官方文档对这两种关系的说明以及更多官方的例子以加深理解: