django的Models用法

模型是關(guān)于您的數(shù)據(jù)的單一的、確定的信息來(lái)源。它包含您存儲(chǔ)的數(shù)據(jù)的基本字段和行為。通常,每個(gè)模型映射到單個(gè)數(shù)據(jù)庫(kù)表。

基礎(chǔ)知識(shí):

  • 每個(gè)模型都是一個(gè) Python 的類(lèi),這些類(lèi)繼承 django.db.models.Model
  • 模型類(lèi)的每個(gè)屬性都相當(dāng)于一個(gè)數(shù)據(jù)庫(kù)的字段。
  • 綜上訴說(shuō),Django 給你一個(gè)自動(dòng)生成訪(fǎng)問(wèn)數(shù)據(jù)庫(kù)的 API;請(qǐng)參閱 Making queries。

簡(jiǎn)單的例子

這個(gè)樣例模型定義了一個(gè) Person, 其擁有 first_name 和 last_name:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

firstnamelastname 是模型的字段。每個(gè)字段都被指定為class屬性,每個(gè)屬性映射到數(shù)據(jù)庫(kù)列。

上面的Person模型會(huì)創(chuàng)建一個(gè)這樣的數(shù)據(jù)庫(kù)表:

CREATE TABLE myapp_person (
    "id" serial NOT NULL PRIMARY KEY,
    "first_name" varchar(30) NOT NULL,
    "last_name" varchar(30) NOT NULL
);

一些技術(shù)上的說(shuō)明:

  • 該表的名稱(chēng) “myapp_person” 是自動(dòng)從某些模型元數(shù)據(jù)中派生出來(lái),但可以被改寫(xiě)。有關(guān)更多詳細(xì)信息,請(qǐng)參閱:表命名。
  • 一個(gè) id 字段會(huì)被自動(dòng)添加,但是這種行為可以被改寫(xiě)。請(qǐng)參閱:默認(rèn)主鍵字段。
  • The CREATE TABLE SQL in this example is formatted using PostgreSQL syntax, but it's worth noting Django uses SQL tailored to the database backend specified in your settings file.

使用模型

一旦你定義了你的模型,你需要告訴Django你將會(huì)使用這些模型。通過(guò)編輯你的設(shè)置文件和改變 INSTALLED_APPS 設(shè)置來(lái)添加包含你的 models.py 的模塊的名稱(chēng)來(lái)實(shí)現(xiàn)這一點(diǎn)。

例如,如果您的應(yīng)用程序的模型存在于模塊myapp.models(為應(yīng)用程序創(chuàng)建的包結(jié)構(gòu) manage.py startapp 腳本),INSTALLED_APPS** 應(yīng)該閱讀部分內(nèi)容:

 INSTALLED_APPS = [
    #...
    'myapp',
    #...
]

當(dāng)你添加新的APP到 INSTALLED_APPS, 去報(bào)先執(zhí)行manage.py migrate,有時(shí)候需要限制性 manage.py makemigrations.

字段

模型中最重要的部分——以及模型中唯一需要的部分——是它定義的數(shù)據(jù)庫(kù)字段列表。字段由class屬性指定。注意不要選擇與模型API沖突的字段名,如clean, save 或者 delete.

例子

from django.db import models

class Musician(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    instrument = models.CharField(max_length=100)

class Album(models.Model):
     artist = models.ForeignKey(Musician, on_delete=models.CASCADE)
    name = models.CharField(max_length=100)
    release_date = models.DateField()
    num_stars = models.IntegerField()

字段類(lèi)型

模型中的每個(gè)字段都應(yīng)該是適當(dāng)字段類(lèi)的一個(gè)實(shí)例。Django使用field類(lèi)類(lèi)型來(lái)確定一些事情:

  • 列類(lèi)型,它告訴數(shù)據(jù)庫(kù)要存儲(chǔ)什么樣的數(shù)據(jù)(例如INTEGER、VARCHAR、TEXT)。
  • 默認(rèn)的HTML小部件在呈現(xiàn)表單字段時(shí)使用(例如:<input type="text">,<select>)。
  • 在Django的管理員和自動(dòng)生成的表單中使用的最小驗(yàn)證需求。

Django配備了數(shù)十種內(nèi)置的字段類(lèi)型;您可以在模型字段引用中找到完整的列表。如果Django的內(nèi)置函數(shù)不奏效,您可以輕松地編寫(xiě)自己的字段;參見(jiàn)編寫(xiě)定制模型字段。

字段選項(xiàng)

每個(gè)字段都接受一組特定于字段的參數(shù)(在模型字段引用中記錄)。例如,CharField(及其子類(lèi))需要一個(gè) max_length 參數(shù),該參數(shù)指定用于存儲(chǔ)數(shù)據(jù)的VARCHAR 數(shù)據(jù)庫(kù)字段的大小。

對(duì)于所有字段類(lèi)型,也有一組通用的參數(shù)。都是可選的。它們?cè)趨⒖嘉墨I(xiàn)中得到了充分的解釋?zhuān)@里有一個(gè)對(duì)最常用的解釋的快速總結(jié):

null

如果是True,Django會(huì)將空置的值存儲(chǔ)為 NULL。默認(rèn)是False。

blank

如果是True,這個(gè)字段是空白的。默認(rèn)是False。

注意,這與null不同。null與數(shù)據(jù)庫(kù)相關(guān),而blank則是與驗(yàn)證相關(guān)的。如果一個(gè)字段有 blank=True ,表單驗(yàn)證就允許輸入空值。如果一個(gè)字段有blank=False ,則需要字段。

choices

2元組的可迭代(例如,列表或元組),用作此字段的選項(xiàng)。如果給出了這個(gè),則默認(rèn)表單小部件將是一個(gè)選擇框而不是標(biāo)準(zhǔn)文本字段,并將限制對(duì)給定選項(xiàng)的選擇。

選擇列表如下:

YEAR_IN_SCHOOL_CHOICES = (
    ('FR', 'Freshman'),
    ('SO', 'Sophomore'),
    ('JR', 'Junior'),
    ('SR', 'Senior'),
    ('GR', 'Graduate'),
)

每個(gè)元組中的第一個(gè)元素是儲(chǔ)存在數(shù)據(jù)庫(kù)中的值。第二個(gè)元素由field的form小部件顯示。

給定一個(gè)模型實(shí)例,可以使用 get_FOO_display()方法訪(fǎng)問(wèn)帶有選擇的字段的顯示值。例如:

from django.db import models

class Person(models.Model):
    SHIRT_SIZES = (
        ('S', 'Small'),
        ('M', 'Medium'),
        ('L', 'Large'),
    )
    name = models.CharField(max_length=60)
    shirt_size = models.CharField(max_length=1, choices=SHIRT_SIZES)
image.png

如果將models 加載到admin后臺(tái),顯示如下:


image.png

default
字段的默認(rèn)值。這可以是一個(gè)值或者一個(gè)可調(diào)用的對(duì)象。如果每次創(chuàng)建新對(duì)象時(shí)都將調(diào)用callable。

help_text
額外的“幫助”文本將顯示在form小部件中。即使你的字段沒(méi)有在表單上使用,它對(duì)文檔也很有用。

 name = models.CharField(max_length=60, default=None, help_text="個(gè)人姓名")
代碼
前端展示

primary_key

如果True,此字段是模型的主鍵

如果你沒(méi)有指定你的模型中的任何字段的primarykey=True,Django會(huì)自動(dòng)添加IntegerField來(lái)保存主鍵,所以除非你想要覆蓋默認(rèn)的主鍵行為,否則你不需要在任何字段上設(shè)置primarykey=True。更多信息,請(qǐng)參見(jiàn)自動(dòng)主鍵字段。

主鍵字段是只讀的。如果您將主鍵的值更改為現(xiàn)存對(duì)象,然后保存它,那么將會(huì)在舊物件旁邊創(chuàng)建一個(gè)新物件。例如:

from django.db import models

class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)

unique
如果是真的,這個(gè)字段必須在整個(gè)表中是唯一的。


自動(dòng)主鍵字段

在默認(rèn)情況下,Django提供了以下字段:

id = models.AutoField(primary_key=True)

這是一個(gè)自動(dòng)遞增的主鍵。

如果您想要指定一個(gè)定制的主鍵,請(qǐng)?jiān)谀囊粋€(gè)字段中指定 primarykey=True。如果Django看到你已經(jīng)明確地設(shè)置了字段。主鍵,它不會(huì)添加自動(dòng)id列。

每個(gè)模型只需要一個(gè)字段來(lái)?yè)碛衟rimarykey=True(要么顯式聲明,要么自動(dòng)添加)。


詳細(xì)字段名稱(chēng)

除了和 之外ForeignKey, 每個(gè)字段類(lèi)型都采用可選的第一個(gè)位置參數(shù) - 一個(gè)詳細(xì)的名稱(chēng)。如果沒(méi)有給出詳細(xì)名稱(chēng),Django將使用字段的屬性名稱(chēng)自動(dòng)創(chuàng)建它,將下劃線(xiàn)轉(zhuǎn)換為空格。
ManyToManyFieldOneToOneField

  • 在此示例中,詳細(xì)名稱(chēng)為:"person's first name"

first_name = models.CharField("person's first name", max_length=30)

code
前端展示
  • 在此示例中,詳細(xì)名稱(chēng)為:"first name"

first_name = models.CharField(max_length=30)

ForeignKey, ManyToManyField and OneToOneField 都要求第一個(gè)參數(shù)是一個(gè)模型類(lèi),所以使用verbosename關(guān)鍵字參數(shù):

poll = models.ForeignKey(
    Poll,
    on_delete=models.CASCADE,
    verbose_name="the related poll",
)
sites = models.ManyToManyField(Site, verbose_name="list of sites")
place = models.OneToOneField(
    Place,
    on_delete=models.CASCADE,
    verbose_name="related place",
)

公約并不是要利用**verbose_name的第一個(gè)字母。Django會(huì)自動(dòng)將第一個(gè)字母大寫(xiě)。


關(guān)聯(lián)關(guān)系

顯然,關(guān)系數(shù)據(jù)庫(kù)的功能在于將表相互關(guān)聯(lián)起來(lái)。Django提供了定義三種最常見(jiàn)的數(shù)據(jù)庫(kù)關(guān)系類(lèi)型的方法:多對(duì)一、多對(duì)多和一對(duì)一。


多對(duì)一

要定義多對(duì)一關(guān)系,請(qǐng)使用django.db.models.ForeignKey。您可以像使用任何其他Field類(lèi)型一樣使用它:將其包含為模型的類(lèi)屬性。

ForeignKey 需要一個(gè)位置參數(shù):模型相關(guān)的類(lèi)。

例如,如果一個(gè)“汽車(chē)”模型有一個(gè)“制造商”——也就是說(shuō),“制造商”生產(chǎn)多輛汽車(chē),但每輛車(chē)都只有一個(gè)“制造商”——使用以下定義:

from django.db import models

class Manufacturer(models.Model):
    # ...
    pass

class Car(models.Model):
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.CASCADE)
    # ...

多對(duì)多

要定義多對(duì)多關(guān)系,請(qǐng)使用 ManyToManyField。您可以像使用任何其他Field類(lèi)型一樣使用它 :將其包含為模型的類(lèi)屬性。

ManyToManyField 需要一個(gè)位置參數(shù):模型相關(guān)的類(lèi)。

例如,如果一個(gè)“pizza”有多個(gè)“topping”的對(duì)象——也就是說(shuō),“topping”可以在多個(gè)pizza上,每個(gè)“pizza”都有多種topping——以下是你如何表示:

class Topping(models.Model):
    # ...
    pass

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)</pre>

多對(duì)多關(guān)系中的額外字段

當(dāng)您只處理簡(jiǎn)單的多對(duì)多關(guān)系時(shí),例如混合和匹配比薩餅和澆頭,ManyToManyField您只需要一個(gè)標(biāo)準(zhǔn) 。但是,有時(shí)您可能需要將數(shù)據(jù)與兩個(gè)模型之間的關(guān)系相關(guān)聯(lián)。

例如,考慮應(yīng)用程序跟蹤音樂(lè)家所屬的音樂(lè)組的情況。一個(gè)人與他們所屬的團(tuán)體之間存在多對(duì)多的關(guān)系,因此您可以使用aManyToManyField來(lái)表示這種關(guān)系。但是,您可能希望收集的成員資格有很多詳細(xì)信息,例如此人加入該組的日期。

對(duì)于這些情況,Django允許您指定將用于管理多對(duì)多關(guān)系的模型。然后,您可以在中間模型上添加額外的字段。中間模型與ManyToManyField使用 through參數(shù)指向?qū)⒊洚?dāng)中介的模型相關(guān)聯(lián) 。對(duì)于我們的音樂(lè)家示例,代碼看起來(lái)像這樣:

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)

設(shè)置中間模型時(shí),您明確指定多對(duì)多關(guān)系中涉及的模型的外鍵。此顯式聲明定義了兩個(gè)模型的關(guān)聯(lián)方式。

中間模型有一些限制:

  • 您的中間模型必須包含一個(gè) - 且只有一個(gè) - 源模型的外鍵(這將Group在我們的示例中),或者您必須顯式指定Django應(yīng)該用于關(guān)系的外鍵ManyToManyField.through_fields。如果您有多個(gè)外鍵through_fields但未指定,則會(huì)引發(fā)驗(yàn)證錯(cuò)誤。類(lèi)似的限制適用于目標(biāo)模型的外鍵(這將Person在我們的示例中)。
  • 對(duì)于通過(guò)中間模型與自身具有多對(duì)多關(guān)系的模型,允許同一模型的兩個(gè)外鍵,但它們將被視為多對(duì)多關(guān)系的兩個(gè)(不同)側(cè)。如果有更多的比兩個(gè)外鍵雖然,你還必須指定through_fields如上,或驗(yàn)證錯(cuò)誤將得到提升。
  • 在使用中間模型定義從模型到自身的多對(duì)多關(guān)系時(shí),必須使用 symmetrical=False(請(qǐng)參閱 模型字段引用)。

現(xiàn)在您已經(jīng)設(shè)置了ManyToManyField使用中間模型(Membership在本例中),您已準(zhǔn)備好開(kāi)始創(chuàng)建一些多對(duì)多關(guān)系。您可以通過(guò)創(chuàng)建中間模型的實(shí)例來(lái)完成此操作:

image.png

一對(duì)一

定義一對(duì)一, use OneToOneField. 您可以像任何其他“字段”類(lèi)型一樣使用它:將其包含為模型的類(lèi)屬性。

當(dāng)該對(duì)象以某種方式“擴(kuò)展”另一個(gè)對(duì)象時(shí),這在對(duì)象的主鍵上是最有用的。

OneToOneField 需要一個(gè)位置參數(shù):模型相關(guān)的類(lèi)。

例如,如果您正在構(gòu)建“地點(diǎn)”數(shù)據(jù)庫(kù),您將在數(shù)據(jù)庫(kù)中構(gòu)建非常標(biāo)準(zhǔn)的內(nèi)容,例如地址,電話(huà)號(hào)碼等。然后,如果你想在這些地方建立一個(gè)餐館數(shù)據(jù)庫(kù),而不是重復(fù)自己并在Restaurant模型中復(fù)制這些字段,你可以做Restaurant一個(gè)OneToOneFieldto Place(因?yàn)橐粋€(gè)餐館“是一個(gè)”地方;事實(shí)上,處理這通常使用 繼承,它涉及隱式的一對(duì)一關(guān)系)。

與此同時(shí)ForeignKey,可以定義遞歸關(guān)系,并且可以對(duì)尚未定義的模型進(jìn)行引用


跨文件的模型

將模型與另一個(gè)應(yīng)用程序中的模型相關(guān)聯(lián)是完全可以的。為此,請(qǐng)?jiān)诙x模型的文件頂部導(dǎo)入相關(guān)模型。然后,只需在需要的地方引用其他模型類(lèi)。例如:

from django.db import models
from geography.models import ZipCode

class Restaurant(models.Model):
    # ...
    zip_code = models.ForeignKey(
        ZipCode,
        on_delete=models.SET_NULL,
        blank=True,
        null=True,
    )

字段名稱(chēng)限制

jango對(duì)模型字段名稱(chēng)只有兩個(gè)限制:

  1. 字段名稱(chēng)不能是Python保留字,因?yàn)檫@會(huì)導(dǎo)致Python語(yǔ)法錯(cuò)誤。例如:

    class Example(models.Model):
       pass = models.IntegerField() # 'pass' is a reserved word!
    
  2. 由于Django的查詢(xún)查找語(yǔ)法的工作方式,字段名稱(chēng)不能在一行中包含多個(gè)下劃線(xiàn)。例如:

    class Example(models.Model):
      foo__bar = models.IntegerField() # 'foo__bar' has two underscores!
    

但是,這些限制可以解決,因?yàn)槟淖侄蚊Q(chēng)不一定必須與您的數(shù)據(jù)庫(kù)列名稱(chēng)匹配。請(qǐng)參閱 db_column選項(xiàng)。

SQL保留字(例如join,whereselect允許作為模型字段名稱(chēng),因?yàn)镈jango會(huì)轉(zhuǎn)義每個(gè)基礎(chǔ)SQL查詢(xún)中的所有數(shù)據(jù)庫(kù)表名和列名。它使用特定數(shù)據(jù)庫(kù)引擎的引用語(yǔ)法。


Meta選項(xiàng)

使用內(nèi)部提供模型元數(shù)據(jù),如下所示:class Meta

class Ox(models.Model):
    horn_length = models.IntegerField()

    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"

模型元數(shù)據(jù)是“任何不是字段的東西”,例如排序選項(xiàng)(ordering),數(shù)據(jù)庫(kù)表名(db_table)或人類(lèi)可讀的單數(shù)和復(fù)數(shù)名稱(chēng)(verbose_nameverbose_name_plural)。不需要,添加到模型是完全可選的。class Meta

Meta可以在模型選項(xiàng)參考中找到所有可能選項(xiàng)的完整列表。


模型屬性

objects
模型最重要的屬性是 Manager。它是為Django模型提供數(shù)據(jù)庫(kù)查詢(xún)操作的接口,用于 從數(shù)據(jù)庫(kù)中檢索實(shí)例。如果Manager未定義自定義,則默認(rèn)名稱(chēng)為 objects。管理員只能通過(guò)模型??類(lèi)訪(fǎng)問(wèn),而不能通過(guò)模型??實(shí)例訪(fǎng)問(wèn)。


模型方法

在模型上定義自定義方法,以向?qū)ο筇砑幼远x“行級(jí)”功能。雖然Manager方法旨在執(zhí)行“表格范圍”的事情,但模型方法應(yīng)該作用于特定的模型實(shí)例。

這是將業(yè)務(wù)邏輯保存在一個(gè)地方的有價(jià)值的技術(shù) - 模型

例如,此模型有一些自定義方法:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=50)
    last_name = models.CharField(max_length=50)
    birth_date = models.DateField()

    def baby_boomer_status(self):
        "Returns the person's baby-boomer status."
        import datetime
        if self.birth_date < datetime.date(1945, 8, 1):
            return "Pre-boomer"
        elif self.birth_date < datetime.date(1965, 1, 1):
            return "Baby boomer"
        else:
            return "Post-boomer"

    @property
    def full_name(self):
        "Returns the person's full name."
        return '%s %s' % (self.first_name, self.last_name)

此示例中的最后一個(gè)方法是屬性。

模型實(shí)例參考具有的完整列表,自動(dòng)給每個(gè)模型的方法。您可以覆蓋其中的大多數(shù) - 請(qǐng)參閱下面的覆蓋預(yù)定義模型方法 - 但有幾個(gè)您幾乎總是想要定義:

__str__()

Python“魔術(shù)方法”,返回任何對(duì)象的字符串表示形式。這是Python和Django在模型實(shí)例需要被強(qiáng)制并顯示為純字符串時(shí)將使用的內(nèi)容。最值得注意的是,當(dāng)您在交互式控制臺(tái)或管理員中顯示對(duì)象時(shí)會(huì)發(fā)生這種情況。

你總是想要定義這個(gè)方法; 默認(rèn)情況下根本沒(méi)有用。

get_absolute_url()

這告訴Django如何計(jì)算對(duì)象的URL。Django在其管理界面中使用它,并且只要它需要找出對(duì)象的URL。

具有唯一標(biāo)識(shí)它的URL的任何對(duì)象都應(yīng)定義此方法。


繼承模型

模型繼承在Django中與普通類(lèi)繼承在Python中的工作方式幾乎完全相同,但也仍有遵循本頁(yè)開(kāi)頭的內(nèi)容。這意味著其基類(lèi)應(yīng)該繼承自django.db.models.Model。

您必須做出的唯一決定是您是希望父模型本身是模型(使用自己的數(shù)據(jù)庫(kù)表),還是父母只是通過(guò)子模型可見(jiàn)的公共信息的持有者。

Django中有三種可能的繼承方式。

  1. 通常,您只想使用父類(lèi)來(lái)保存您不希望為每個(gè)子模型鍵入的信息。這個(gè)類(lèi)不會(huì)被孤立使用,所以抽象基類(lèi)就是你所追求的。
  2. 如果你是現(xiàn)有模型的子類(lèi)(可能是完全來(lái)自另一個(gè)應(yīng)用程序的東西),并希望每個(gè)模型都有自己的數(shù)據(jù)庫(kù)表,那么 多表繼承是最佳選擇。
  3. 最后,如果您只想修改模型的Python級(jí)行為,而不以任何方式更改模型字段,則可以使用 代理模型。

抽象基類(lèi)

當(dāng)您想要將一些公共信息放入許多其他模型時(shí),抽象基類(lèi)非常有用。你寫(xiě)你的基類(lèi),并把abstract=True 類(lèi)。然后,此模型將不用于創(chuàng)建任何數(shù)據(jù)庫(kù)表。相反,當(dāng)它用作其他模型的基類(lèi)時(shí),其字段將添加到子類(lèi)的字段中。

一個(gè)例子:

from django.db import models

class CommonInfo(models.Model):
    name = models.CharField(max_length=100)
    age = models.PositiveIntegerField()

    class Meta:
        abstract = True

class Student(CommonInfo):
    home_group = models.CharField(max_length=5)

Student模型將有三個(gè)領(lǐng)域:name,agehome_group。該CommonInfo模型不能用作普通的Django模型,因?yàn)樗且粋€(gè)抽象基類(lèi)。它不生成數(shù)據(jù)庫(kù)表或具有管理器,并且無(wú)法直接實(shí)例化或保存。

從抽象基類(lèi)繼承的字段可以使用其他字段或值覆蓋,也可以使用刪除None。

對(duì)于許多用途,這種類(lèi)型的模型繼承將完全符合您的要求。它提供了一種在Python級(jí)別分解公共信息的方法,同時(shí)仍然只在數(shù)據(jù)庫(kù)級(jí)別為每個(gè)子模型創(chuàng)建一個(gè)數(shù)據(jù)庫(kù)表。

Meta繼承

當(dāng)創(chuàng)建抽象基類(lèi)時(shí),Django使 您在基類(lèi)中聲明的任何Meta內(nèi)部類(lèi)可用作屬性。如果子類(lèi)沒(méi)有聲明自己的Meta 類(lèi),它將繼承父類(lèi)的Meta。如果孩子想要擴(kuò)展父類(lèi)的Meta類(lèi),它可以將其子類(lèi)化。例如:

from django.db import models

class CommonInfo(models.Model):
    # ...
    class Meta:
        abstract = True
        ordering = ['name']

class Student(CommonInfo):
    # ...
    class Meta(CommonInfo.Meta):
        db_table = 'student_info'

Django確實(shí)對(duì)抽象基類(lèi)的Meta類(lèi)進(jìn)行了一次調(diào)整:在安裝Meta屬性之前,它設(shè)置了abstract=False。這意味著抽象基類(lèi)的子項(xiàng)本身不會(huì)自動(dòng)成為抽象類(lèi)。當(dāng)然,您可以創(chuàng)建一個(gè)繼承自另一個(gè)抽象基類(lèi)的抽象基類(lèi)。您只需要記住abstract=True每次都明確設(shè)置。

在抽象基類(lèi)的Meta類(lèi)中包含一些屬性是沒(méi)有意義的。例如,包含db_table意味著所有子類(lèi)(未指定自己的Meta)將使用相同的數(shù)據(jù)庫(kù)表,這幾乎肯定不是您想要的。


多表繼承

Django支持的第二種模型繼承是當(dāng)層次結(jié)構(gòu)中的每個(gè)模型都是模型本身時(shí)。每個(gè)模型對(duì)應(yīng)于自己的數(shù)據(jù)庫(kù)表,可以單獨(dú)查詢(xún)和創(chuàng)建。繼承關(guān)系引入子模型與其每個(gè)父模型之間的鏈接(通過(guò)自動(dòng)創(chuàng)建OneToOneField)。例如:

from django.db import models

class 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)

盡管數(shù)據(jù)將駐留在不同的數(shù)據(jù)庫(kù)表 PlaceRestaurant,但所有字段都將可用。所以這些都是可能的:

Place.objects.filter(name="Bob's Cafe")
Restaurant.objects.filter(name="Bob's Cafe")

如果你的a Place 也是a Restaurant,你可以使用模型名稱(chēng)的小寫(xiě)版本從 Place對(duì)象到Restaurant對(duì)象:

image.png

但是,如果p在上面的示例中不是 a Restaurant(它已直接創(chuàng)建為Place對(duì)象或是其他類(lèi)的父級(jí)),則引用p.restaurant會(huì)引發(fā)Restaurant.DoesNotExist 異常。

自動(dòng)創(chuàng)建OneToOneFieldRestaurant,它鏈接到Place看起來(lái)像這樣:

place_ptr = models.OneToOneField(
    Place, on_delete=models.CASCADE,
    parent_link=True,
)

Meta和多表繼承

在多表繼承情況下,子類(lèi)從其父類(lèi)的Meta類(lèi)繼承是沒(méi)有意義的。所有的Meta選項(xiàng)都已經(jīng)應(yīng)用于父類(lèi),并且再次應(yīng)用它們通常只會(huì)導(dǎo)致矛盾的行為(這與基類(lèi)本身不存在的抽象基類(lèi)情況形成對(duì)比)。

因此,子模型無(wú)法訪(fǎng)問(wèn)其父級(jí)的Meta類(lèi)。但是,有一些有限的情況,子進(jìn)程從父進(jìn)程繼承行為:如果子進(jìn)程沒(méi)有指定 ordering屬性或get_latest_by屬性,它將從其父進(jìn)程繼承它們。

如果父級(jí)有一個(gè)排序而你不希望孩子有任何自然順序,你可以明確地禁用它:

class ChildModel(ParentModel):
    # ...
    class Meta:
        # Remove parent's ordering effect
        ordering = []

代理模型

使用多表繼承時(shí),會(huì)為模型的每個(gè)子類(lèi)創(chuàng)建一個(gè)新的數(shù)據(jù)庫(kù)表。這通常是所需的行為,因?yàn)樽宇?lèi)需要一個(gè)位置來(lái)存儲(chǔ)基類(lèi)上不存在的任何其他數(shù)據(jù)字段。但是,有時(shí)您只想更改模型的Python行為 - 可能更改默認(rèn)管理器或添加新方法。

這就是代理模型繼承的用途:為原始模型創(chuàng)建代理。您可以創(chuàng)建,刪除和更新代理模型的實(shí)例,并且將保存所有數(shù)據(jù),就像使用原始(非代理)模型一樣。不同之處在于您可以更改代理中的默認(rèn)模型排序或默認(rèn)管理器等內(nèi)容,而無(wú)需更改原始內(nèi)容。

代理模型聲明為普通模型。你通過(guò)設(shè)置類(lèi)的proxy屬性告訴Django它是一個(gè)代理模型。

例如,假設(shè)您要向Person模型添加方法。你可以這樣做:

from django.db import models

class Person(models.Model):
    first_name = models.CharField(max_length=30)
    last_name = models.CharField(max_length=30)

class MyPerson(Person):
    class Meta:
        proxy = True

    def do_something(self):
        # ...
        pass

MyPerson班在同一個(gè)數(shù)據(jù)庫(kù)表作為它的父工作 Person類(lèi)。特別是,任何新的實(shí)例 Person 也可以通過(guò) MyPerson,反之亦然:

image.png

你仍然可以使用一個(gè)代理模型來(lái)定義模型的默認(rèn)排序方法,你也許不會(huì)想一直對(duì)“Person”進(jìn)行排序,但是通常情況下用代理模型根據(jù)“姓氏”屬性進(jìn)行排序這很簡(jiǎn)單。:

class OrderedPerson(Person):
    class Meta:
        ordering = ["last_name"]
        proxy = True

基類(lèi)限制

一個(gè)代理模型必須僅能繼承一個(gè)非抽象模型類(lèi)。你不能繼承多個(gè)非抽象模型類(lèi),因?yàn)榇砟P蜔o(wú)法提供不同數(shù)據(jù)表的任何行間連接。一個(gè)代理模型可以繼承任意數(shù)量的抽象模型類(lèi),假如他們沒(méi)有定義任何的模型字段。一個(gè)代理模型也可以繼承任意數(shù)量的代理模型,只需他們共享同一個(gè)非抽象父類(lèi)


模型代理管理器

如果未在代理模型上指定任何模型管理器,它將從其模型父項(xiàng)繼承管理器。如果您在代理模型上定義管理器,它將成為默認(rèn)管理器,盡管在父類(lèi)上定義的任何管理器仍然可用。

繼續(xù)上面的示例,您可以更改查詢(xún)Person模型時(shí)使用的默認(rèn)管理器,如下所示:

from django.db import models

class NewManager(models.Manager):
    # ...
    pass

class MyPerson(Person):
    objects = NewManager()

    class Meta:
        proxy = True

如果要在不更換現(xiàn)有默認(rèn)值的情況下向代理添加新管理器,可以使用自定義管理器文檔中描述的技術(shù):創(chuàng)建包含新管理器的基類(lèi),并在主基類(lèi)之后繼承:

# Create an abstract class for the new manager.
class ExtraManagers(models.Model):
    secondary = NewManager()

    class Meta:
        abstract = True

class MyPerson(Person, ExtraManagers):
    class Meta:
        proxy = True

多重繼承

正如Python的子類(lèi)化一樣,Django模型可以從多個(gè)父模型繼承。請(qǐng)記住,正常的Python名稱(chēng)解析規(guī)則適用。特定名稱(chēng)(例如Meta)出現(xiàn)的第一個(gè)基類(lèi)將是使用的基類(lèi); 例如,這意味著如果多個(gè)父類(lèi)包含一個(gè)Meta類(lèi),則只會(huì)使用第一個(gè)類(lèi),而將忽略所有其他類(lèi)。

通常,您不需要從多個(gè)父級(jí)繼承。這有用的主要用例是“混入”類(lèi):向每個(gè)繼承混合的類(lèi)添加特定的額外字段或方法。盡量使您的繼承層次結(jié)構(gòu)盡可能簡(jiǎn)單明了,這樣您就不必費(fèi)力去找出特定信息來(lái)自哪里。

請(qǐng)注意,從具有公共id主鍵字段的多個(gè)模型繼承將引發(fā)錯(cuò)誤。要正確使用多重繼承,可以AutoField在基本模型中使用explici

class Article(models.Model):
    article_id = models.AutoField(primary_key=True)
    ...

class Book(models.Model):
    book_id = models.AutoField(primary_key=True)
    ...

class BookReview(Book, Article):
    pass
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀(guān)點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容