- 本文將介紹Django的MVT模型 中的model 也就是ORM 通過python操縱數據庫
- model 定義了數據的結構 也就是 列屬性 列約束 列數據類型等等
- 本文將幫你學習以下內容…
- 0 簡單認識MVT
- 1 認識model
- 2 如何對數據庫進行 增刪改查
- 3 metadata method的概念
MVT模式
其實就是MVC模式的簡單改造?
MVT
- M model 數據庫結構模型 說白了就是懶得用sql 而是用面嚮對象語言(python&Django js&NodeJS)操作數據庫
- V view 直接翻譯是視圖 但其實意思是 渲染視圖的執行者
渲染視圖 就是給html模板填空 模板固定 但是上面的數據隨着用戶請求的不同而不同
類似 百度搜索 網址導航不同 模板一樣(就是一個個網址導航排成列)數據不同(只要你搜索關鍵詞不同)
所以 View 是視圖的靈魂 - template 這就是html模板 動態網站的一大特徵——以不變應萬變 —— 以不變的模板 動態的數據 應萬變的用戶需求
MVC模式
- M 還是 model 同上
- V 還是view 可以看做MVT的 VT合併起來的東西 因爲不僅僅負責數據 還有靜態的模板
MVC的V真的是全權負責 顯示 能看的見的 這一部分 - C controller 控制器 負責頁面邏輯 這裏就類似MVT中V的部分功能——頁面邏輯 比如鼠標位置影響某些動態內容的加載等等
其實就是MVT中VT職能的劃分的不同 我們沒必要糾結 尤其是小規模的網站
好 背景補充完了
下面 我們就來學習model 也即是ORM —— 如何用python django 操作數據庫
field 列屬性
這裏我們聊一下 如何通過python 制定我們表的結構(就是加條件:)
學過MySQL的朋友知道 表的結構其實就是通過DDL 數據定義語言 我在我MySQL專欄強調過多次
也就是 我們只需要限定
- 列屬性的名稱(真名或者
AS
的別名) - 列屬性的數據類型 比如整形 浮點
- 列屬性的約束 比如
- 默認是NULL
- 列屬性的索引 就是添加Unique唯一鍵 或者主鍵 外鍵等等
我們看看語法
my_field_name = models.CharField(max_length=20, help_text='Enter field documentation')
也就是 我們想往列屬性上加條件 往CharField( )裏面塞就行了
那麼有這些“條件”名稱:
- max_length 這個列屬性 最多可容納的字符數 注意 是字符
- help_text 解釋一下這個列屬性幹啥用的
- verbose_name: 別名
- default: 默認值
- null: 決定是否默認值爲NULL (null = true 是)
- blank: 當爲true 可以值爲空 意思就是你不往這裏輸入一個值 他就會報錯:)
- choices: 這個類似ENUM屬性 或者是CHECK約束 比如性別 只有男 女 兩個選項
- primary_key: 主鍵 也就是 唯一 而且不爲NULL的鍵(可能含有多列 這經常被考到 套路我們)而且django設計的 如果沒有主鍵 他會給我們自動安上一個2333
主鍵 索引概念有點忘記的朋友可以參考:數據庫學習之MySQL (二十六)—— DDL(四)約束 - 主鍵 外鍵詳解 添加約束 刪除約束 修改約束
我們看看這堆玩意兒 說白了還不是用原生sql概念可以解決的東西 (又包裝一遍真快樂)
那麼我們ORM 也就是用py控制sql的方法也就是這幾個方面
- 名稱
- verbose_name 別名 主要是方便我們 “顧名思義”
比如 ISDN 和 the_ISDN_of_books 真名 和 別名 - help_text
- verbose_name 別名 主要是方便我們 “顧名思義”
- 約束(constrain)
- null 當null屬性爲true
- default
- blank
- primary_key
- choices
- 數據類型
- max_length
這樣歸類對於學過sql語句的朋友可能更加清晰
還有別的屬性 我建議沒必要記太多 畢竟原生sql纔是關鍵2333
需要的時候請查詢 django的說明
不知道你有沒有觀察到他用的函數“CharField” 那我用"IntField" 行不?
對了!django把數據類型有關的條件放到函數裏面去了:
django函數 | 解釋 | 對應的sql數據類型 |
---|---|---|
CharField | 放字符串 注意要制定最大字長 | char (注意不是varchar 這玩意固定的) |
TextField | 長字符串(比如博文) | text |
IntegerField | 存整形 | integer |
DateField、DateTimeField | 時間日期數據 | date time |
EmailField | 存email地址的 | - |
FileField、ImageField | 存文件 存圖片 | blob |
AutoField | 自增的那個約束 | auto_increment 注意不是increasement |
還有兩個 羣體對羣體的條件 我們後面會講
- ForeignKey
- ManyToManyField
metadata
django官方文檔
meta 元 metadata元數據 就是一些 描述數據的數據(發明這個概念的人真矯情)
說白了 我們想通過ORM查出數據 找一條條記錄
但是 查出的記錄是以什麼樣的順序呢? 從大到小?從A-Z?
我們就用一個數據 描述它 這就是元數據的作用之一 非常類似ORDER BY
class meta:
ordering = ['title', '-pubdate']
這個非常類似下面這段sql代碼:
ORDER BY title ASC, pubdate DESC;
很明顯 代碼通過title作爲第一排序 然後當title相同的時候 就用pubdate排序
注意 對於char 排序採用A-Z 對於數字 排序按大小 對於日期 排序按先後
然後django寫法中 用-
表示 DESC 也就是默認是升序的(ASC) 然後加-
成了 降序(DESC)
另外 元數據的另一個作用是 給這個類起別名
verbose_name = 'BetterName'
還可以弄一個抽象類 也就是 我們如果多個表格都是同樣的套路(限制條件) 就可以統一繼承於一個抽象類的下面 這樣就更加高效簡單 需要修改 重載一下就好
這個我們後面會講到
注意 meta 既然是描述數據的數據 自然是隸屬於某一個類(也就是對應一個表)
method
每個model都會有一些方法method 有些方法比較常用 比如:
__str__
這樣就能給對象返回一個我們能夠讀的懂的字符串 作爲說明之用get_absolute_urls
這樣就能提供給你一個能夠顯示獨立model的記錄的url 我們作爲admin 管理起來更加方便
所以大概是這麼寫:
def __str__(self):
return self.field_name
def get_absolute_url(self):
"""Returns the url to access a particular instance of the model."""
return reverse('model-detail-view', args=[str(self.id)])
這個也不用擔心 後面會詳細講的
ORM操作 model管理
說白了 這裏就是將一些基本的 通過ORM 操作數據庫的方式 也就是DQL DDL DML TCL的封裝而已
也就是 增 刪 改 查CRUD
# Create a new record using the model's constructor.
record = MyModelName(my_field_name="Instance #1")
# Save the object into the database.
record.save()
# Change record by modifying the fields, then calling save().
record.my_field_name = "New Instance Name"
# Save the object into the database.
record.save()
第一句 增 生成model並且填值
第二句 生效到數據庫 類似 commit
第三句 更改數據(就是沒有生效的事務)
第四句 commit
注意你的思維方式 列就是一個對象的屬性 我們的記錄只是個對象實例 表就是這種對象關係
然後 我們用fliter 類似WHERE的篩選條件 作爲DQL 查詢記錄:
# 所有結果 沒有應用fliter()篩選的結果
all_books = Book.objects.all()
# title裏面含有‘wild’的記錄 也就是 類似sql 的LIKE關鍵字
wild_books = Book.objects.filter(title__contains='wild')
# 類似sql的count() 函數
number_wild_books = wild_books.count()
還有更多可以使用的API 不過這裏限於篇幅不講太多 後面會專門歸類整理的 放心
或者 傳送官方文檔:
空談誤國 實幹興邦
我們先複習一下:
面向對象概念 | sql概念 | 實際位置 |
---|---|---|
class 類 | relation 關係 | table 表 |
attribute 屬性 | field 欄位 字段 | column 列 |
instance 實例 | record 記錄 | row 行 |
然後 應用所學知識於我們的圖書館項目:
我們可以這麼構思:
我們需要以下的表:
- 書名(一種書的表)
- 具體的書(copies的管理)
- 書籍分類表(genre)
- 作者的表
然後 在models.py /locallibrary/catalog/model.py
裏面 我們創建
from django.db import models
from django.urls import reverse # Used to generate URLs by reversing the URL patterns
import uuid # Required for unique book instances
# 書的作者 模型
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('Died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
return f'{self.last_name}, {self.first_name}'
# 書籍分類模型
class Genre(models.Model):
name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
def __str__(self):
return self.name
# 書種類的模型 注意是一種書 不是一本書(copy)
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True)
summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
isbn = models.CharField('ISBN', max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
def __str__(self):
"""String for representing the Model object."""
return self.title
def get_absolute_url(self):
"""Returns the url to access a detail record for this book."""
return reverse('book-detail', args=[str(self.id)])
# 書實體的模型 也就是具體的一本書
class BookInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
LOAN_STATUS = (
('m', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='m',
help_text='Book availability',
)
class Meta:
ordering = ['due_back']
def __str__(self):
return f'{self.id} ({self.book.title})'
思考一下爲啥這幾個類的放置順序是這樣的 後面我們會說明的:)
下面我們詳細介紹一下 算是對前面內容的複習補充:
- 書籍分類
class Genre(models.Model):
name = models.CharField(max_length=200, help_text='Enter a book genre (e.g. Science Fiction)')
def __str__(self):
"""String for representing the Model object."""
return "《"+self.name+"》"
這個就是一個列是名字(name) 最大長度爲200字符 然後還有個__str__
用於返回說明文字 這裏偷懶 就添加一個書名號了
- 書的種類 也就是不管印刷了多少本 都算一種
class Book(models.Model):
title = models.CharField(max_length=200)
author = models.ForeignKey(Author, on_delete=models.SET_NULL, null=True)
summary = models.TextField(max_length=1000, help_text='Enter a brief description of the book')
isbn = models.CharField('ISBN', max_length=13, help_text='13 Character <a href="https://www.isbn-international.org/content/what-isbn">ISBN number</a>')
genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
def __str__(self):
"""String for representing the Model object."""
return self.title
def get_absolute_url(self):
"""Returns the url to access a detail record for this book."""
return reverse('book-detail', args=[str(self.id)])
- title那個就是定長字符串
- 外鍵foreign key的應用 類似於數學的映射 多對一 一個第一作者 有多個作品 然而 一本書只有一個 第一作者
於是外鍵就是 在作品這方(“多”)可以引用 作者方(“一”)
因爲作者就相當於標籤 比如 我們對一羣學生作區分 研究生 本科生 專科生 是三個標籤 但是學生有千千萬萬 於是我們學生的表格就應該引用 只有三個標籤(應該是標籤表中的記錄)
同樣 我們給書打標籤 也是如此 - ManyToManyField 多對多 這是外鍵的拓展 一本書可以已有多個領域分類(genre) 而一個領域分類肯定包含多本書
那麼 我們也不用操心具體多對多的流程 —— 寫上genre = models.ManyToManyField(Genre, help_text='Select a genre for this book')
這句完事
- 具體的書
- 如果把 一種書作爲類 那麼具體的書就是書的實例
- 當然我們還是定義了類描述“書的實例”
import uuid # Required for unique book instances
class BookInstance(models.Model):
id = models.UUIDField(primary_key=True, default=uuid.uuid4, help_text='Unique ID for this particular book across whole library')
book = models.ForeignKey(Book, on_delete=models.SET_NULL, null=True)
imprint = models.CharField(max_length=200)
due_back = models.DateField(null=True, blank=True)
LOAN_STATUS = (
('m', 'Maintenance'),
('o', 'On loan'),
('a', 'Available'),
('r', 'Reserved'),
)
status = models.CharField(
max_length=1,
choices=LOAN_STATUS,
blank=True,
default='m',
help_text='Book availability',
)
class Meta:
ordering = ['due_back']
def __str__(self):
"""String for representing the Model object."""
return f'{self.id} ({self.book.title})'
- id的定義用了 UUIDField 也就是我們定義主鍵的時候可以這麼用
- 當然有時候我們想定義爲非主鍵的列 那樣的話就要修改一下 (否則可能報錯):
- 可以爲空的列
範例:bookuuid = models.UUIDField(default=uuid.uuid4, null=False,verbose_name=u’book_verbose _id’,help_text=“book verbose id”) - 不可爲空的列
範例:bookuuid = models.UUIDField(default=None, null=True, blank=True,verbose_name=u’book_verbose_id’,help_text=“book_verbose_id”)
- 可以爲空的列
- 當然有時候我們想定義爲非主鍵的列 那樣的話就要修改一下 (否則可能報錯):
- book imprint due_back 正常套路
- LOAN_STATUS 這個就類似ENUM 指示status只能有4種情況
- 然後 class meta 指示了 查詢輸出記錄的 order by 順序
__str__
正常套路
- 作者表
class Author(models.Model):
first_name = models.CharField(max_length=100)
last_name = models.CharField(max_length=100)
date_of_birth = models.DateField(null=True, blank=True)
date_of_death = models.DateField('Died', null=True, blank=True)
class Meta:
ordering = ['last_name', 'first_name']
def get_absolute_url(self):
return reverse('author-detail', args=[str(self.id)])
def __str__(self):
return f'{self.last_name}, {self.first_name}'
return reverse('author-detail', args=[str(self.id)])
注意這句 reverse的意思就是 “反過來”
還記得我們的url映射嘛 我們是把url映射到 資源實際地址或者能夠返回資源的對象(url.py 上寫的“處理url請求的套路”)上去
這裏我們反過來 通過套路author-detail
得到請求的url
然後呢?
就是進行數據庫遷移啦:
python3 manage.py makemigrations
python3 manage.py migrate
總結 ´◡`
本節我們玩了一下django的數據庫操作工具 也就是ORM
但是應該如何修改這些數據庫呢?
下一節我們來通過“後臺” admin頁面進行修改
然後到後期才你能做出來 提供多用戶 的前端客戶端的修改