Django模型詳解
Django模型詳解
目錄
Model (模型) 簡而言之即資料模型,是一個Django應用的核心。模型不是資料本身(比如資料表裡的資料), 而是抽象的描述資料的構成和邏輯關係。
每個Django的模型(model)實際上是個類,繼承了models.Model
。每個Model應該包括屬性(欄位),關係(比如單對單,單對多和多對多)和方法。當你定義好Model模型後,Django的介面會自動幫你在資料庫生成相應的資料表(table)。這樣你就不用自己用SQL語言建立表格或在資料庫裡操作建立表格了,是不是很省心?
模型定義小案例
假設你要開發一個名叫bookstore
的應用,專門來管理書店裡的書籍。我們首先要為書本和出版社建立模型。出版社有名字和地址。書有名字,描述和新增日期。我們還需要利用ForeignKey定義了出版社與書本之間單對多的關係,因為一個出版社可以出版很多書,每本書都有對應的出版社。我們定義了Publisher
和Book
模型,它們都繼承了models.Model
。你能看出程式碼有什麼問題嗎?
# models.py
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField()
def __str__(self):
return self.name
class Book(models.Model):
name = models.CharField(max_length=30)
description = models.TextField(blank=True, null=True)
publisher = ForeignKey(Publisher)
add_date = models.DateField()
def __str__(self):
return self.name
模型建立好後,當你執行python manage.py migrate
命令建立資料表的時候你會遇到錯誤,錯誤原因如下:
-
CharField
裡的max_length
選項沒有定義 -
ForeignKey(Publisher)
裡的on_delete
選項有沒有定義
所以當你定義Django模型Model的時候,你一定要十分清楚2件事:
-
這個Field是否有必選項, 比如
CharField
的max_length
和ForeignKey
的on_delete
選項是必須要設定的。 -
這個Field是否必需(blank = True or False),是否可以為空 (null = True or False)。這關係到資料的完整性。
下面是訂正錯誤後的Django模型:
# models.py
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=60)
def __str__(self):
return self.name
class Book(models.Model):
name = models.CharField(max_length=30)
description = models.TextField(blank=True, default='')
publisher = ForeignKey(Publisher,on_delete=models.CASCADE)
add_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
修改模型後,你需要連續執行python manage.py makemigrations
和python manage.py migrate
這兩個命令,前者檢查模型有無變化,後者將變化遷移至資料表。如果一切順利,Django會在資料庫(預設sqlite)中生成或變更由appname_modelname
組成的資料表,本例兩張資料表分別為bookstore_publisher
和bookstore_book
。
模型的組成
一個標準的Django模型分別由模型欄位、META選項和方法三部分組成。我們接下來對各部分進行詳細介紹。Django官方編碼規範建議按如下方式排列:
- 定義的模型欄位:包括基礎欄位和關係欄位
- 自定義的Manager方法:改變模型
-
class Meta選項
: 包括排序、索引等等(可選)。 -
def __str__()
:定義單個模型例項物件的名字(可選)。 -
def save()
:重寫save方法(可選)。 -
def get_absolute_url()
:為單個模型例項物件生成獨一無二的url(可選) - 其它自定義的方法。
模型的欄位
models.Model
提供的常用模型欄位包括基礎欄位和關係欄位。
基礎欄位
**CharField() **
一般需要通過max_length = xxx 設定最大字元長度。如不是必填項,可設定blank = True和default = ‘‘。如果用於username, 想使其唯一,可以設定unique = True
。如果有choice選項,可以設定 choices = XXX_CHOICES
**TextField() **
適合大量文字,max_length = xxx選項可選。
**DateField() 和DateTimeField() **
可通過default=xx選項設定預設日期和時間。
- 對於DateTimeField: default=timezone.now - 先要
from django.utils import timezone
- 如果希望自動記錄一次修改日期(modified),可以設定:
auto_now=True
- 如果希望自動記錄建立日期(created),可以設定
auto_now_add=True
**EmailField() **
如不是必填項,可設定blank = True和default = ‘。一般Email用於使用者名稱應該是唯一的,建議設定unique = True
IntegerField(), SlugField(), URLField(),BooleanField()
可以設定blank = True or null = True。對於BooleanField一般建議設定defaut = True or False
**FileField(upload_to=None, max_length=100) - 檔案欄位 **
- upload_to = “/some folder/”:上傳資料夾路徑
- max_length = xxxx:檔案最大長度
**ImageField (upload_to=None, max_length=100,)- 圖片欄位 **
- upload_to = “/some folder/”: 指定上傳圖片路徑
關係欄位
OneToOneField(to, on_delete=xxx, options) - 單對單關係
- to必需指向其他模型,比如 Book or ‘self’ .
- 必需指定
on_delete
選項(刪除選項): i.e, “on_delete = models.CASCADE
” or “on_delete = models.SET_NULL
” . - 可以設定 “
related_name = xxx
” 便於反向查詢。
ForeignKey(to, on_delete=xxx, options) - 單對多關係
- to必需指向其他模型,比如 Book or ‘self’ .
- 必需指定
on_delete
選項(刪除選項): i.e, “on_delete = models.CASCADE
” or “on_delete = models.SET_NULL
” . - 可以設定”default = xxx” or “null = True” ;
- 如果有必要,可以設定 “
limit_choices_to =
“, - 可以設定 “
related_name = xxx
” 便於反向查詢。
ManyToManyField(to, options) - 多對多關係
- to 必需指向其他模型,比如 User or ‘self’ .
- 設定 “
symmetrical = False
“ 表示多對多關係不是對稱的,比如A關注B不代表B關注A - 設定 “
through = 'intermediary model'
“ 如果需要建立中間模型來蒐集更多資訊。 - 可以設定 “
related_name = xxx
” 便於反向查詢。
示例:一個人加入多個組,一個組包含多個人,我們需要額外的中間模型記錄加入日期和理由。
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self):
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self):
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person, on_delete=models.CASCADE)
group = models.ForeignKey(Group, on_delete=models.CASCADE)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
對於OneToOneField
和ForeignKey
, on_delete
選項和related_name
是兩個非常重要的設定,前者決定了了關聯外來鍵刪除方式,後者決定了模型反向查詢的名字。
on_delete刪除選項
Django提供瞭如下幾種關聯外來鍵刪除選項, 可以根據實際需求使用。
-
CASCADE
:級聯刪除。當你刪除publisher記錄時,與之關聯的所有 book 都會被刪除。 -
PROTECT
: 保護模式。如果有外來鍵關聯,就不允許刪除,刪除的時候會丟擲ProtectedError錯誤,除非先把關聯了外來鍵的記錄刪除掉。例如想要刪除publisher,那你要把所有關聯了該publisher的book全部刪除才可能刪publisher。 -
SET_NULL
: 置空模式。刪除的時候,外來鍵欄位會被設定為空。刪除publisher後,book 記錄裡面的publisher_id 就置為null了。 -
SET_DEFAULT
: 置預設值,刪除的時候,外來鍵欄位設定為預設值。 -
SET()
: 自定義一個值。 -
DO_NOTHING
:什麼也不做。刪除不報任何錯,外來鍵值依然保留,但是無法用這個外來鍵去做查詢。
related_name選項
related_name
用於設定模型反向查詢的名字,非常有用。在文初的Publisher
和Book
模型裡,我們可以通過book.publisher
獲取每本書的出版商資訊,這是因為Book
模型裡有publisher
這個欄位。但是Publisher
模型裡並沒有book
這個欄位,那麼我們如何通過出版商反查其出版的所有書籍資訊呢?
Django對於關聯欄位預設使用模型名_set
進行反查,即通過publisher.book_set.all
查詢。但是book_set
並不是一個很友好的名字,我們更希望通過publisher.books
獲取一個出版社已出版的所有書籍資訊,這時我們就要修改我們的模型了,將related_name
設為books
, 如下所示:
# models.py
from django.db import models
class Publisher(models.Model):
name = models.CharField(max_length=30)
address = models.CharField(max_length=60)
def __str__(self):
return self.name
# 將related_name設定為books
class Book(models.Model):
name = models.CharField(max_length=30)
description = models.TextField(blank=True, default='')
publisher = ForeignKey(Publisher,on_delete=models.CASCADE, related_name='books')
add_date = models.DateField(auto_now_add=True)
def __str__(self):
return self.name
我們再來對比一下如何通過publisher查詢其出版的所有書籍,你覺得哪個更好呢?
- 設定
related_name
前:publisher.book_set.all
- 設定
related_name
後:publisher.books.all
模型的META選項
-
abstract=True
: 指定該模型為抽象模型 -
proxy=True
: 指定該模型為代理模型 -
verbose_name=xxx
和verbose_name_plural=xxx
: 為模型設定便於人類閱讀的別名 -
db_table= xxx
: 自定義資料表名 -
odering=['-pub-date']
: 自定義按哪個欄位排序,-
代表逆序 -
permissions=[]
: 為模型自定義許可權 -
managed=False
: 預設為True,如果為False,Django不會為這個模型生成資料表 -
indexes=[]
: 為資料表設定索引,對於頻繁查詢的欄位,建議設定索引 -
constraints=
: 給資料庫中的資料表增加約束。
模型的方法
標準方法
以下三個方法是Django模型自帶的三個標準方法:
-
def __str__()
:給單個模型物件例項設定人為可讀的名字(可選)。 -
def save()
:重寫save方法(可選)。 -
def get_absolute_url()
:為單個模型例項物件生成獨一無二的url(可選)
除此以外,我們經常自定義方法或Manager方法
示例一:自定義方法
# 為每篇文章生成獨一無二的url
def get_absolute_url(self):
return reverse('blog:article_detail', args=[str(self.id)])
# 計數器
def viewed(self):
self.views += 1
self.save(update_fields=['views'])
示例二:自定義Manager方法
# First, define the Manager subclass.
class DahlBookManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(author='Roald Dahl')
# Then hook it into the Book model explicitly.
class Book(models.Model):
title = models.CharField(max_length=100)
author = models.CharField(max_length=50)
objects = models.Manager() # The default manager.
dahl_objects = DahlBookManager() # The Dahl-specific manager.
完美的高階Django模型示例
一個完美的django高階模型結構如下所示,可以滿足絕大部分應用場景,希望對你有所幫助。
from django.db import models
from django.urls import reverse
# 自定義Manager方法
class HighRatingManager(models.Manager):
def get_queryset(self):
return super().get_queryset().filter(rating=1)
# CHOICES選項
class Rating(models.IntegerChoices):
VERYGOOD = 1, 'Very Good'
GOOD = 2, 'Good'
BAD = 3, 'Bad'
class Product(models.Model):
# 資料表字段
name = models.CharField('name', max_length=30)
rating = models.IntegerField(max_length=1, choices=Rating.choices)
# MANAGERS方法
objects = models.Manager()
high_rating_products =HighRatingManager()
# META類選項
class Meta:
verbose_name = 'product'
verbose_name_plural = 'products'
# __str__方法
def __str__(self):
return self.name
# 重寫save方法
def save(self, *args, **kwargs):
do_something()
super().save(*args, **kwargs)
do_something_else()
# 定義單個物件絕對路徑
def get_absolute_url(self):
return reverse('product_details', kwargs={'pk': self.id})
# 其它自定義方法
def do_something(self):