1. 程式人生 > 其它 >Django模型詳解

Django模型詳解

Django模型詳解

目錄

  1. 模型定義小案例
  2. 模型的組成
  3. 模型的欄位
    1. 基礎欄位
    2. 關係欄位
    3. on_delete刪除選項
    4. related_name選項
  4. 模型的META選項
  5. 模型的方法
    1. 標準方法
    2. 示例一:自定義方法
    3. 示例二:自定義Manager方法
  6. 完美的高階Django模型示例
  7. 小結

Model (模型) 簡而言之即資料模型,是一個Django應用的核心。模型不是資料本身(比如資料表裡的資料), 而是抽象的描述資料的構成和邏輯關係。

每個Django的模型(model)實際上是個類,繼承了models.Model。每個Model應該包括屬性(欄位),關係(比如單對單,單對多和多對多)和方法。當你定義好Model模型後,Django的介面會自動幫你在資料庫生成相應的資料表(table)。這樣你就不用自己用SQL語言建立表格或在資料庫裡操作建立表格了,是不是很省心?

模型定義小案例

假設你要開發一個名叫bookstore的應用,專門來管理書店裡的書籍。我們首先要為書本和出版社建立模型。出版社有名字和地址。書有名字,描述和新增日期。我們還需要利用ForeignKey定義了出版社與書本之間單對多的關係,因為一個出版社可以出版很多書,每本書都有對應的出版社。我們定義了PublisherBook模型,它們都繼承了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是否有必選項, 比如CharFieldmax_lengthForeignKeyon_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 makemigrationspython manage.py migrate這兩個命令,前者檢查模型有無變化,後者將變化遷移至資料表。如果一切順利,Django會在資料庫(預設sqlite)中生成或變更由appname_modelname組成的資料表,本例兩張資料表分別為bookstore_publisherbookstore_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)

對於OneToOneFieldForeignKeyon_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用於設定模型反向查詢的名字,非常有用。在文初的PublisherBook模型裡,我們可以通過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查詢其出版的所有書籍,你覺得哪個更好呢?

  1. 設定related_name前:publisher.book_set.all
  2. 設定related_name後:publisher.books.all

模型的META選項

  • abstract=True: 指定該模型為抽象模型
  • proxy=True: 指定該模型為代理模型
  • verbose_name=xxxverbose_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):