HelloDjango 系列教程:創建 Django 博客的數據庫模型

HelloDjango 系列教程:創建 Django 博客的數據庫模型
文中涉及的示例代碼,已同步更新到 HelloGitHub-Team 倉庫

設計博客的數據庫表結構
博客最主要的功能就是展示我們寫的文章,它需要從某個地方獲取博客文章數據才能把文章展示出來,通常來說這個地方就是數據庫。我們把寫好的文章永久地保存在數據庫裏,當用戶訪問我們的博客時,django 就去數據庫裏把這些數據取出來展現給用戶。

博客的文章應該含有標題、正文、作者、發表時間等數據。一個更加現代化的博客文章還希望它有分類、標籤、評論等。爲了更好地存儲這些數據,我們需要合理地組織數據庫的表結構。

我們的博客初級版本主要包含博客文章,文章會有分類以及標籤。一篇文章只能有一個分類,但可以打上很多標籤。

數據庫存儲的數據其實就是表格的形式,例如存儲博客文章的數據庫表長這個樣子:

文章 id 標題 正文 發表時間 分類 標籤
1 title 1 text 1 2019-7-1 django django 學習
2 title 2 text 2 2019-7-2 django django 學習
3 title 3 text 3 2019-7-3 Python Python 學習
其中文章 ID 是一個數字,唯一對應着一篇文章。當然還可以有更多的列以存儲更多相關數據,這只是一個最基本的示例。

數據庫表設計成這樣其實已經可以了,但是稍微分析一下我們就會發現一個問題,這 3 篇文章的分類和標籤都是相同的,這會產生很多重複數據,當數據量很大時就浪費了存儲空間。

不同的文章可能它們對應的分類或者標籤是相同的,所以我們把分類和標籤提取出來,做成單獨的數據庫表,再把文章和分類、標籤關聯起來。下面分別是分類和標籤的數據庫表:

分類 id 分類名
1 Django
2 Python
標籤 id 標籤名
1 Django 學習
2 Python 學習
編寫博客模型代碼
以上是自然語言描述的表格,數據庫也和編程語言一樣,有它自己的一套規定的語法來生成上述的表結構,這樣我們才能把數據存進去。一般來說這時候我們應該先去學習數據庫創建表格的語法,再回來寫我們的 django 博客代碼了。但是 django 告訴我們不用這麼麻煩,它已經幫我們做了一些事情。django 把那一套數據庫的語法轉換成了 Python 的語法形式,我們只要寫 Python 代碼就可以了,django 會把 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)

Category 就是一個標準的 Python 類,它繼承了 models.Model 類,類名爲 Category 。Category 類有一個屬性 name,它是 models.CharField 的一個實例。

這樣,django 就可以把這個類翻譯成數據庫的操作語言,在數據庫裏創建一個名爲 category 的表格,這個表格的一個列名爲 name。還有一個列 id,雖然沒有顯示定義,但 django 會爲我們自動創建。可以看出從 Python 代碼翻譯成數據庫語言時其規則就是一個 Python 類對應一個數據庫表格,類名即表名,類的屬性對應着表格的列,屬性名即列名。

我們需要 3 個表格:文章(Post)、分類(Category)以及標籤(Tag),下面就來分別編寫它們對應的 Python 類。模型的代碼通常寫在相關應用的 models.py 文件裏。已經在代碼中做了詳細的註釋,說明每一句代碼的含義。但如果你在移動端下閱讀不便的話,也可以跳到代碼後面看正文的裏的講解。

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 是字符型,
CharField 的 max_length 參數指定其最大長度,超過這個長度的分類名就不能被存入數據庫。
當然 django 還爲我們提供了多種其它的數據類型,如日期時間類型 DateTimeField、整數類型 IntegerField 等等。
django 內置的全部類型可查看文檔:
https://docs.djangoproject.com/en/2.2/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 要求我們必須存入數據,否則就會報錯。
# 指定 CharField 的 blank=True 參數值後就可以允許空值了。
excerpt = models.CharField(max_length=200, blank=True)

# 這是分類與標籤,分類與標籤的模型我們已經定義在上面。
# 我們在這裏把文章對應的數據庫表和分類、標籤對應的數據庫表關聯了起來,但是關聯形式稍微有點不同。
# 我們規定一篇文章只能對應一個分類,但是一個分類下可以有多篇文章,所以我們使用的是 ForeignKey,即一
# 對多的關聯關係。且自 django 2.0 以後,ForeignKey 必須傳入一個 on_delete 參數用來指定當關聯的
# 數據被刪除時,被關聯的數據的行爲,我們這裏假定當某個分類被刪除時,該分類下全部文章也同時被刪除,因此     # 使用 models.CASCADE 參數,意爲級聯刪除。
# 而對於標籤來說,一篇文章可以有多個標籤,同一個標籤下也可能有多篇文章,所以我們使用 
# ManyToManyField,表明這是多對多的關聯關係。
# 同時我們規定文章可以沒有標籤,因此爲標籤 tags 指定了 blank=True。
# 如果你對 ForeignKey、ManyToManyField 不瞭解,請看教程中的解釋,亦可參考官方文檔:
# https://docs.djangoproject.com/en/2.2/topics/db/models/#relationships
category = models.ForeignKey(Category, on_delete=models.CASCADE)
tags = models.ManyToManyField(Tag, blank=True)

# 文章作者,這裏 User 是從 django.contrib.auth.models 導入的。
# django.contrib.auth 是 django 內置的應用,專門用於處理網站用戶的註冊、登錄等流程,User 是 
# django 爲我們已經寫好的用戶模型。
# 這裏我們通過 ForeignKey 把文章和 User 關聯了起來。
# 因爲我們規定一篇文章只能有一個作者,而一個作者可能會寫多篇文章,因此這是一對多的關聯關係,和 
# Category 類似。
author = models.ForeignKey(User, on_delete=models.CASCADE)

博客模型代碼代碼詳解
首先是 Category 和 Tag 類,它們均繼承自 model.Model 類,這是 django 規定的。Category 和 Tag 類均有一個name 屬性,用來存儲它們的名稱。由於分類名和標籤名一般都是用字符串表示,因此我們使用了 CharField 來指定 name 的數據類型,同時 max_length 參數則指定 name 允許的最大長度,超過該長度的字符串將不允許存入數據庫。除了 CharField ,django 還爲我們提供了更多內置的數據類型,比如時間類型 DateTimeField、整數類型 IntegerField 等等。

提示:

在本教程中我們會教你這些類型的使用方法,但以後你開發自己的項目時,你就需要通過閱讀 django 官方文檔 關於字段類型的介紹 來了解有哪些數據類型可以使用以及如何使用它們。

Post 類也一樣,必須繼承自 model.Model 類。文章的數據庫表稍微複雜一點,主要是列更多,我們指定了這些列:

title:文章的標題,數據類型是 CharField,允許的最大長度 max_length = 70。

body:文章正文,我們使用了 TextField。比較短的字符串存儲可以使用 CharField,但對於文章的正文來說可能會是一大段文本,因此使用 TextField 來存儲大段文本。

created_time 和 modified_time:這兩個列分別表示文章的創建時間和最後一次修改時間,存儲時間的列用 DateTimeField 數據類型。

excerpt:文章摘要,可以沒有文章摘要,但默認情況下 CharField 要求我們必須存入數據,否則就會報錯。指定 CharField 的 blank=True 參數值後就可以允許空值了。

category 和 tags:分類與標籤,分類與標籤的模型我們已經定義在上面。我們把文章對應的數據庫表和分類、標籤對應的數據庫表關聯了起來,但是關聯形式稍微有點不同。我們規定一篇文章只能對應一個分類,但是一個分類下可以有多篇文章,所以我們使用的是 ForeignKey,即一對多的關聯關係。且自 django 2.0 以後,ForeignKey 必須傳入一個 on_delete 參數用來指定當關聯的數據被刪除時,被關聯的數據的行爲,我們這裏假定當某個分類被刪除時,該分類下全部文章也同時被刪除,因此使用 models.CASCADE 參數,意爲級聯刪除。

而對於標籤來說,一篇文章可以有多個標籤,同一個標籤下也可能有多篇文章,所以我們使用 ManyToManyField,表明這是多對多的關聯關係。同時我們規定文章可以沒有標籤,因此爲標籤 tags 指定了 blank=True。

author:文章作者,這裏 User 是從 django.contrib.auth.models 導入的。django.contrib.auth 是 django 內置的應用,專門用於處理網站用戶的註冊、登錄等流程。其中 User 是 django 爲我們已經寫好的用戶模型,和我們自己編寫的 Category 等類是一樣的。這裏我們通過 ForeignKey 把文章和 User關聯了起來,因爲我們規定一篇文章只能有一個作者,而一個作者可能會寫多篇文章,因此這是一對多的關聯關係,和 Category 類似。

理解多對一和多對多兩種關聯關係
我們分別使用了兩種關聯數據庫表的形式:ForeignKey和 ManyToManyField。

ForeignKey
ForeignKey 表明一種一對多的關聯關係。比如這裏我們的文章和分類的關係,一篇文章只能對應一個分類,而一個分類下可以有多篇文章。反應到數據庫表格中,它們的實際存儲情況是這樣的:

文章 ID 標題 正文 分類 ID
1 title 1 body 1 1
2 title 2 body 2 1
3 title 3 body 3 1
4 title 4 body 4 2
分類 ID 分類名
1 Django
2 Python
可以看到文章和分類實際上是通過文章數據庫表中 分類 ID 這一列關聯的。當要查詢文章屬於哪一個分類時,只需要查看其對應的分類 ID 是多少,然後根據這個分類 ID 就可以從分類數據庫表中找到該分類的數據。例如這裏文章 1、2、3 對應的分類 ID 均爲 1,而分類 ID 爲 1 的分類名爲 django,所以文章 1、2、3 屬於分類 django。同理文章 4 屬於分類 Python。

反之,要查詢某個分類下有哪些文章,只需要查看對應該分類 ID 的文章有哪些即可。例如這裏 django 的分類 ID 爲 1,而對應分類 ID 爲 1 的文章有文章 1、2、3,所以分類 django 下有 3 篇文章。

希望這個例子能幫助你加深對多對一關係,以及它們在數據庫中是如何被關聯的理解,更多的例子請看文末給出的 django 官方參考資料。

ManyToManyField
ManyToManyField 表明一種多對多的關聯關係,比如這裏的文章和標籤,一篇文章可以有多個標籤,而一個標籤下也可以有多篇文章。反應到數據庫表格中,它們的實際存儲情況是這樣的:

文章 ID 標題 正文
1 title 1 body 1
2 title 2 body 2
3 title 3 body 3
4 title 4 body 4
標籤 ID 標籤名
1 Django 學習
2 Python 學習
文章 ID 標籤 ID
1 1
1 2
2 1
3 2
多對多的關係無法再像一對多的關係中的例子一樣在文章數據庫表加一列 分類 ID 來關聯了,因此需要額外建一張表來記錄文章和標籤之間的關聯。例如文章 ID 爲 1 的文章,既對應着 標籤 ID 爲 1 的標籤,也對應着 標籤 ID 爲 2 的標籤,即文章 1 既屬於標籤 1:django 學習,也屬於標籤 2:Python 學習。

反之,標籤 ID 爲 1 的標籤,既對應着 文章 ID 爲 1 的文章,也對應着 文章 ID 爲 2 的文章,即標籤 1:django 學習下有兩篇文章。

希望這個例子能幫助你加深對多對多關係,以及它們在數據庫中是如何被關聯的理解,更多的例子請看文末給出的 django 官方參考資料。

假如你對多對一關係和多對多關係還存在一些困惑,強烈建議閱讀官方文檔對這兩種關係的說明以及更多官方的例子以加深理解:

django ForeignKey 簡介
django ForeignKey 詳細示例
django ManyToManyField 簡介
django ManyToManyField 詳細示例
作者:削微寒
出處:https://www.cnblogs.com/xueweihan/

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章