4. 創(chuàng)建 django 博客的數(shù)據(jù)庫(kù)模型

本教程內(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ō)明以及更多官方的例子以加深理解:

django ForeignKey 簡(jiǎn)介

django ForeignKey 詳細(xì)示例

django ManyToManyField 簡(jiǎn)介

django ManyToManyField 詳細(xì)示例

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容