本教程內(nèi)容已過(guò)時(shí),更新版教程請(qǐng)?jiān)L問(wèn): Django 博客開(kāi)發(fā)入門(mén)教程。
這是 Django 博客教程的第 4 篇,在閱讀此篇教程以前,請(qǐng)確保你已閱讀 Django 博客教程的前 3 篇:
1. Django 博客教程:前言
2. 搭建開(kāi)發(fā)環(huán)境
3. 建立我們的 django 博客應(yīng)用
編寫(xiě)博客的數(shù)據(jù)庫(kù)模型
博客最主要的功能就是展示我們寫(xiě)的文章,它需要從某個(gè)地方獲取我們寫(xiě)的博客文章數(shù)據(jù)才能把它展示出來(lái),通常來(lái)說(shuō)這個(gè)地方就是數(shù)據(jù)庫(kù)。我們把寫(xiě)好的文章永久地保存在數(shù)據(jù)庫(kù)里,當(dāng)用戶訪問(wèn)我們的博客時(shí),django 就去數(shù)據(jù)庫(kù)里把這些數(shù)據(jù)取出來(lái)展現(xiàn)給用戶。
博客的文章應(yīng)該含有標(biāo)題、正文、作者、發(fā)表時(shí)間等數(shù)據(jù),一個(gè)更加現(xiàn)代化的博客文章我們也希望它有分類(lèi)、標(biāo)簽、評(píng)論等。為了更好地存儲(chǔ)這些數(shù)據(jù),我們需要合理地組織我們數(shù)據(jù)庫(kù)的結(jié)構(gòu)。
我們的博客初級(jí)版本主要包含這些數(shù)據(jù):博客文章,文章會(huì)有分類(lèi)以及標(biāo)簽。一篇文章只能有一個(gè)分類(lèi),但可以打上很多標(biāo)簽。數(shù)據(jù)庫(kù)存儲(chǔ)的數(shù)據(jù)其實(shí)就是表格的形式,例如存儲(chǔ)我們的博客文章的數(shù)據(jù)庫(kù)表長(zhǎng)這個(gè)樣子:
| 文章 id | 標(biāo)題 | 正文 | 發(fā)表時(shí)間 | 分類(lèi) | 標(biāo)簽 |
|---|---|---|---|---|---|
| 1 | title1 | text1 | 2016-12-23 | django | django 學(xué)習(xí) |
| 2 | title2 | text2 | 2016-12-24 | django | django 學(xué)習(xí) |
| 3 | title3 | text3 | 2016-12-26 | Python | Python 學(xué)習(xí) |
其中 id 是一個(gè)數(shù)字,唯一對(duì)應(yīng)著一篇文章。當(dāng)然還可以在列上加入更多的信息,這只是一個(gè)最基本的示例。
數(shù)據(jù)庫(kù)表設(shè)計(jì)成這樣其實(shí)已經(jīng)可以了,但是稍微分析一下我們就會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題,這 3 篇文章的分類(lèi)和標(biāo)簽都是相同的,這會(huì)產(chǎn)生很多重復(fù)數(shù)據(jù),當(dāng)數(shù)據(jù)量很大時(shí)就浪費(fèi)了存儲(chǔ)空間。不同的文章可能它們的分類(lèi)或者標(biāo)簽是相同的,所以我們把分類(lèi)和標(biāo)簽?zāi)贸鰜?lái),做成單獨(dú)的數(shù)據(jù)庫(kù)表,再把文章和分類(lèi)與標(biāo)簽關(guān)聯(lián)起來(lái)就可以了。下面分別是分類(lèi)和標(biāo)簽的數(shù)據(jù)庫(kù)表:
| 分類(lèi) id | 分類(lèi)名 |
|---|---|
| 1 | django |
| 2 | Python |
| 標(biāo)簽 id | 標(biāo)簽名 |
|---|---|
| 1 | django 學(xué)習(xí) |
| 2 | Python 學(xué)習(xí) |
以上是自然語(yǔ)言描述的表格,數(shù)據(jù)庫(kù)也和編程語(yǔ)言一樣,有它自己的一套規(guī)定的語(yǔ)法來(lái)生成上述的表格結(jié)構(gòu),這樣我們才能把數(shù)據(jù)存進(jìn)去。一般情況下這時(shí)候我們應(yīng)該先去學(xué)習(xí)數(shù)據(jù)庫(kù)創(chuàng)建表格的語(yǔ)法,再回來(lái)寫(xiě)我們的博客程序了。但是 django 跟我們說(shuō)不用這么麻煩,我已經(jīng)幫你做了一些事情,我把那一套數(shù)據(jù)庫(kù)的語(yǔ)法轉(zhuǎn)換成了 Python 的語(yǔ)法形式,你寫(xiě)你的 Python 代碼就可以了,翻譯的工作我來(lái)幫你。用更加專(zhuān)業(yè)一點(diǎn)的說(shuō)法,就是 django 為我們提供了一套 ORM(Object Relational Mapping)系統(tǒng)。比如說(shuō)我們的分類(lèi)數(shù)據(jù)庫(kù)表,django 只要求我們這樣寫(xiě):
blog/models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=100)
這就是一個(gè)標(biāo)準(zhǔn)的 Python 的類(lèi),我們繼承了 models.Model 類(lèi),類(lèi)名為 Category(分類(lèi)),Category 類(lèi)有一個(gè)屬性 name,它是 models.CharField 的一個(gè)實(shí)例。這樣,django 就可以把這個(gè)類(lèi)翻譯成數(shù)據(jù)庫(kù)的語(yǔ)法,在數(shù)據(jù)庫(kù)里創(chuàng)建一個(gè)名為 category 的表格,這個(gè)表格的一個(gè)列名為 name(即分類(lèi)名),還有一個(gè)列 id 則會(huì)自動(dòng)幫我們創(chuàng)建。其規(guī)則就是一個(gè) Python 類(lèi)對(duì)應(yīng)一個(gè)數(shù)據(jù)庫(kù)表格,類(lèi)名即表名,類(lèi)的屬性對(duì)應(yīng)著表格的列,屬性名即列名。我們需要 3 個(gè)表格:文章(Post)、分類(lèi)(Category)以及標(biāo)簽(Tag),下面就來(lái)創(chuàng)建它們。已經(jīng)在代碼中做了詳細(xì)的注釋?zhuān)f(shuō)明每一句代碼的含義。但如果你在移動(dòng)端下閱讀不便的話,也可以跳到代碼后面看正文的里的講解。
blog/models.py
from django.db import models
from django.contrib.auth.models import User
class Category(models.Model):
"""
django 要求我們必須繼承 models.Model 類(lèi),
Category 只需要一個(gè)簡(jiǎn)單的分類(lèi)名 name 就可以了。
CharField 指定了 name 的數(shù)據(jù)類(lèi)型,
CharField 是字符型,
max_length 指定其最大長(zhǎng)度,
超過(guò)這個(gè)長(zhǎng)度的分類(lèi)名就不能被存入數(shù)據(jù)庫(kù)。
當(dāng)然 django 還為我們提供了各種各樣的類(lèi)型,
如日期時(shí)間類(lèi)型 DateTimeField、
整數(shù)類(lèi)型 IntegerField 等等。
django 內(nèi)置的類(lèi)型全部類(lèi)型可查看文檔:
https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types
"""
name = models.CharField(max_length=100)
class Tag(models.Model):
"""
標(biāo)簽 Tag 也比較簡(jiǎn)單,
和 Category 一樣。
再次強(qiáng)調(diào)一定要繼承 models.Model 類(lèi)!
"""
name = models.CharField(max_length=100)
class Post(models.Model):
"""
文章的數(shù)據(jù)庫(kù)表稍微復(fù)雜一點(diǎn),主要是涉及的字段更多。
"""
# 文章標(biāo)題
title = models.CharField(max_length=70)
# 文章正文,我們使用了 TextField。
# 比較短的字符串存儲(chǔ)可以使用 CharField,
# 但對(duì)于文章的正文來(lái)說(shuō)可能會(huì)是一大段文本,
# 因此使用 TextField 來(lái)存儲(chǔ)大段文本。
body = models.TextField()
# 這兩個(gè)列分表表示了文章的創(chuàng)建時(shí)間和最后一次修改時(shí)間,
# 存儲(chǔ)時(shí)間的列用 DateTimeField。
created_time = models.DateTimeField()
modified_time = models.DateTimeField()
# 文章摘要,可以沒(méi)有文章摘要,
# 但默認(rèn)情況下 CharField 要求我們必須存入數(shù)據(jù),
# 否則就會(huì)報(bào)錯(cuò)。
# 指定 blank=True 后就可以允許空值了。
excerpt = models.CharField(max_length=200, blank=True)
# 這是分類(lèi)與標(biāo)簽,
# 分類(lèi)與標(biāo)簽的模型我們已經(jīng)定義在上面。
# 我們?cè)谶@里把文章對(duì)應(yīng)的數(shù)據(jù)庫(kù)表和分類(lèi)與標(biāo)簽對(duì)應(yīng)的表關(guān)聯(lián)起來(lái),
# 但是關(guān)聯(lián)形式稍微有點(diǎn)不同。
# 我們規(guī)定一篇文章只能對(duì)應(yīng)一個(gè)分類(lèi),
# 但是一個(gè)分類(lèi)下可以有很多篇文章,
# 所以我們使用的是 ForeignKey,
# 即一對(duì)多的關(guān)系。
# 而對(duì)于標(biāo)簽來(lái)說(shuō),
# 一篇文章可以有多個(gè)標(biāo)簽,
# 同一個(gè)標(biāo)簽下也可能有多篇文章,
# 所以我們使用 ManyToManyField,
# 表明這是多對(duì)多的關(guān)系。
# 同時(shí)我們規(guī)定文章可以沒(méi)有標(biāo)簽,
# 因此為標(biāo)簽 tags 指定了 blank=True。
# 如果你對(duì) ForeignKey、ManyToManyField 不了解,
# 請(qǐng)看教程中的解釋?zhuān)? # 亦可參考官方文檔:
# 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 導(dǎo)入的。
# django.contrib.auth 是 django 內(nèi)置的應(yīng)用,
# 專(zhuān)門(mén)用于處理網(wǎng)站用戶的注冊(cè)、登錄等流程,
# User 是 django 為我們已經(jīng)寫(xiě)好的用戶模型,
# 這里我們通過(guò) ForeignKey 把文章和 User 關(guān)聯(lián)起來(lái),
# 因?yàn)槲覀円?guī)定一篇文章只能有一個(gè)作者,
# 而一個(gè)作者可能會(huì)寫(xiě)多篇文章,
# 因此這是一對(duì)多的關(guān)系,
# 和 Category 類(lèi)似。
author = models.ForeignKey(User)
數(shù)據(jù)庫(kù)模型詳解
首先是 Category(分類(lèi))和 Tag(標(biāo)簽)類(lèi),它們均繼承自 model.Model 類(lèi),這是 django 規(guī)定的。它們均有一個(gè) name 屬性,用來(lái)存儲(chǔ)它們的名稱(chēng)。由于分類(lèi)名和標(biāo)簽名一般都是用字符串表示,因此我們使用了 CharField 來(lái)指定 name 的數(shù)據(jù)類(lèi)型,同時(shí) max_length 參數(shù)則指定 name 的最大長(zhǎng)度。除了 CharField ,django 還為我們提供了更多內(nèi)置的數(shù)據(jù)類(lèi)型,比如時(shí)間類(lèi)型 DateTimeField、整數(shù)類(lèi)型 IntegerField 等等。在本教程中我們會(huì)教你這些類(lèi)型的使用方法,但以后你開(kāi)發(fā)自己的項(xiàng)目時(shí),你就需要通過(guò)閱讀 [django 的官方文檔關(guān)于字段類(lèi)型的介紹][1]來(lái)了解有哪些數(shù)據(jù)類(lèi)型以及如何使用它們。
[1]: https://docs.djangoproject.com/en/1.10/ref/models/fields/#field-types
Post(文章)類(lèi)也一樣,必須繼承自 model.Model 類(lèi)。文章的數(shù)據(jù)庫(kù)表稍微復(fù)雜一點(diǎn),主要是列更多。我們?yōu)樗付诉@些列:
title。這是文章的標(biāo)題,數(shù)據(jù)類(lèi)型是 CharField,最大長(zhǎng)度 max_length = 70。
body。文章正文,我們使用了 TextField。比較短的字符串存儲(chǔ)可以使用 CharField,但對(duì)于文章的正文來(lái)說(shuō)可能會(huì)是一大段文本,因此使用 TextField 來(lái)存儲(chǔ)大段文本。
created_time、modified_time。這兩個(gè)列分表表示了文章的創(chuàng)建時(shí)間和最后一次修改時(shí)間,存儲(chǔ)時(shí)間的列用 DateTimeField 數(shù)據(jù)類(lèi)型
excerpt。文章摘要,可以沒(méi)有文章摘要,但默認(rèn)情況下 CharField 要求我們必須存入數(shù)據(jù),否則就會(huì)報(bào)錯(cuò)。指定 blank=True 后就可以允許空值了。
category 和 tags。這是分類(lèi)與標(biāo)簽,分類(lèi)與標(biāo)簽的模型我們已經(jīng)定義在上面。我們?cè)谶@里把文章對(duì)應(yīng)的數(shù)據(jù)庫(kù)表和分類(lèi)與標(biāo)簽對(duì)應(yīng)的表關(guān)聯(lián)起來(lái),但是關(guān)聯(lián)形式稍微有點(diǎn)不同。我們規(guī)定一篇文章只能對(duì)應(yīng)一個(gè)分類(lèi),但是一個(gè)分類(lèi)下可以有很多篇文章,所以我們使用的是 ForeignKey,即一對(duì)多的關(guān)系。而對(duì)于標(biāo)簽來(lái)說(shuō),一篇文章可以有多個(gè)標(biāo)簽,同一個(gè)標(biāo)簽下也可能有多篇文章,所以我們使用 ManyToManyField,表明這是多對(duì)多的關(guān)系。同時(shí)我們規(guī)定文章可以沒(méi)有標(biāo)簽,因此為標(biāo)簽 tags 指定了 blank=True。
author。文章作者,這里 User 是從 django.contrib.auth.models 導(dǎo)入的。django.contrib.auth 是 django 內(nèi)置的應(yīng)用,專(zhuān)門(mén)用于處理網(wǎng)站用戶的注冊(cè)、登錄等流程,User 是 django 為我們已經(jīng)寫(xiě)好的用戶模型,這里我們通過(guò) ForeignKey 把文章和 User 關(guān)聯(lián)起來(lái),因?yàn)槲覀円?guī)定一篇文章只能有一個(gè)作者,而一個(gè)作者可能會(huì)寫(xiě)多篇文章,因此這是一對(duì)多的關(guān)系,和 Category 類(lèi)似。
注:這里我們使用了兩種關(guān)聯(lián)數(shù)據(jù)庫(kù)表的形式,一種是 ForeignKey,它表明一種一對(duì)多的關(guān)聯(lián)。比如這里我們的文章和分類(lèi)的關(guān)系,一篇文章只能對(duì)應(yīng)一個(gè)分類(lèi),而一個(gè)分類(lèi)下可以有多篇文章。另外一中是 ManyToManyField,看名字就知道這是一種多對(duì)多的關(guān)聯(lián)關(guān)系,比如這里的文章和標(biāo)簽,一篇文章可以有多個(gè)標(biāo)簽,而一個(gè)標(biāo)簽下也可以有多篇文章。假如你對(duì)此有一些困惑,強(qiáng)烈建議閱讀官方文檔對(duì)這兩種關(guān)系的說(shuō)明以及更多官方的例子以加深理解: