Django中三種繼承風格
- 抽象基類:僅將父類用于子類公共信息的載體,這樣的父類永遠都不會單獨使用。
- 多表繼承:繼承了一個模型(可能來源其它應用),且想要每個模型都有對應的數據表。
- 代理模型:只想修改模型的 Python 級行為,而不是以任何形式修改模型字段。
抽象基類
有很多模型需要公共信息的時候,抽象基類就會有用,比如一個后臺管理系統,都需要一些用于審計的字段。
例子:
from django.db import modelsclass CommonInfo(models.Model):name = models.CharField(max_length=100)age = models.PositiveIntegerField()class Meta:abstract = Trueclass Student(CommonInfo):home_group = models.CharField(max_length=5)
關于Meta繼承
當一個抽象基類被建立,Django 將所有你在基類中申明的 Meta 內部類以屬性的形式提供。若子類未定義自己的 Meta 類,它會繼承父類的 Meta。Django 在安裝 Meta 屬性前,對抽象基類的 Meta 做了一個調整——設置 abstract=False。這意味著抽象基類的子類不會自動地變成抽象類。為了繼承一個抽象基類創建另一個抽象基類,你需要在子類上顯式地設置 abstract=True。
由于Python繼承的工作方式,如果子類從多個抽象基類繼承,則默認情況下僅繼承第一個列出的類的 Meta 選項。為了從多個抽象類中繼承 Meta 選項,必須顯式地聲明 Meta 繼承。
關于對 related_name 和 related_query_name 的使用
若你在 外鍵 或 多對多字段 使用了 related_name 或 related_query_name,你必須為該字段提供一個 獨一無二 的反向名字和查詢名字。這在抽象基類中一般會引發問題,因為基類中的字段都被子類繼承,且保持了同樣的值(包括 related_name 和 related_query_name)。
為了解決此問題,當你在抽象基類中(也只能是在抽象基類中)使用 related_name 和 related_query_name,部分值需要包含 ‘%(app_label)s’ 和 ‘%(class)s’。
多表繼承
Django 支持的第二種模型繼承方式是層次結構中的每個模型都是一個單獨的模型。每個模型都指向分離的數據表,且可被獨立查詢和創建,即:多表繼承,它是使用隱式的 OneToOneField 連接子類和父類。
例子:
from django.db import modelsclass Place(models.Model):name = models.CharField(max_length=50)address = models.CharField(max_length=80)class Restaurant(Place):serves_hot_dogs = models.BooleanField(default=False)serves_pizza = models.BooleanField(default=False)
關于Meta的多表繼承
多表繼承情況下,子類不會繼承父類的 Meta。所有 Meta 類選項已被應用至父類,在子類中再次應用會導致行為沖突(與抽象基類中應用場景對比,這種情況下,基類并不存在)。
故,子類模型無法訪問父類的 Meta 類。不過,有限的幾種情況下:若子類未指定 ordering 屬性或 get_latest_by 屬性,子類會從父類繼承這些。
如果父類有排序,而你并不期望子類有排序,你可以顯式的禁止它:
class ChildModel(ParentModel):# ...class Meta:# Remove parent's ordering effectordering = []
代理模型
代理模型繼承的目的:為原模型創建一個代理。你可以創建,刪除和更新代理模型的實例,所以的數據都會存儲的像你使用原模型(未代理的)一樣。不同點是你可以修改代理默認的模型排序和默認管理器,而不需要修改原模型。
代理模型就像普通模型一樣申明。你需要告訴 Django 這是一個代理模型,通過將 Meta 類的 proxy 屬性設置為 True。
例子:
from django.db import modelsclass Person(models.Model):first_name = models.CharField(max_length=30)last_name = models.CharField(max_length=30)class MyPerson(Person):class Meta:proxy = Truedef do_something(self):# ...pass