天天看點

《Django3.0文檔》筆記:Models《Django3.0文檔》筆記:Models

Django3.0

文檔》筆記:Models

模型類

模型可以準确且唯一地描述資料,其包含了存儲資料的重要字段。每個模型都是一個

Python

的類,這些模型類繼承自

django.db.models.Model

,模型類的每個屬性都相當于資料庫表的一個字段。一般來說,每個模型類映射一張資料庫表,每個模型類的執行個體代表資料庫表的一行記錄,每個字段映射為資料庫表的一列。通過模型類和

Django

提供的一套資料庫抽象

API

,可以建立、檢索、更新和删除對象。

from django.db import models


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

樣例代碼定義了一個

Person

模型類,包含

first_name

last_name

兩個字段。根據該模型類可以建立一個如下的資料庫表:

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

字段

模型中最重要且唯一必要的是資料庫的字段定義。字段再類屬性中定義。定義字段名時應小心避免使用與模型

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_data = models.DataField()
    num_stars = models.IntegerField()
           

模型類中每一個字段都應該是某個

Field

字段的執行個體,

Django

利用這些字段類來實作以下功能:

  • 字段類型用以指定資料庫資料類型(如:

    INTEGER

    VARCHAR

    TEXT

    )。
  • 在渲染表單字段時預設使用的

    HTML

    視圖(如:

    <input type='text'>

    <select>

    )。
  • 基本的有效性驗證功能,用于

    Django

    背景和自動生成表單。

每一種字段都需要指定一些特定的參數,例如,

CharField

(以及它的子類)需要接收一個

max_length

參數,用于指定資料庫存儲

VARCHAR

資料時用的位元組數。

一些可選的參數是通用的,可以用于任何字段類型,下面介紹一些經常用到的通用參數:

null

如果設定為

True

,當該字段為空時,

Django

會将資料庫中該字段設定為

NULL

,預設為

False

blank

如果設定為

True

,則該字段允許為空,預設為

False

。(注意:該選項與

null

不同,

null

選項僅僅是資料庫層面的設定,然而

blank

是涉及表單驗證方面。如果一個字段設定為

blank=True

,在進行表單驗證時,接收的資料該字段值允許為空,而設定為

blank=False

時,不允許為空。

choices

一系列二進制組,用作此字段的選項。如果提供了二進制組,預設表單小部件是一個選擇框,而不是标準文本字段,并将限制給出的選項。一個選項清單:

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

每個二進制組的第一個值會存儲在資料庫中,而第二個值将隻會用于在表單中顯示。對于一個模型類的執行個體,要擷取該字段二進制組中相對應的第二個值,使用

get_Foo_display()

方法。例如:

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)
           
>>> p = Person(name="Fred Flintstone", shirt_size="L")
>>> p.save()
>>> p.shirt_size
'L'
>>> p.get_shirt_size_display()
'Large'
           

也可以使用枚舉類以簡潔的方式來定義

choices

:

from django.db import models


class Runner(models.Model):
    MedalType = models.TextChoices('MedalType', 'GOLD SILVER BRONZE')
    name = models.CharField(max_length=60)
    medal = models.CharField(blank=True, choices=MedalType.choices, max_length=10)
           

default

該字段的預設值,可以是一個值或者是個可調用的對象,如果是個可調用對象,每次執行個體化模型時都會調用該對象。

primary_key

如果設定為

True

,将該字段設定為該模式的主鍵。在一個模型類中,如果沒有對任何一個字段設定

primary_key=True

選項,

Django

會自動添加一個

IntegerField

字段,并設定為主鍵。是以可以不手動設定主鍵,也可以手動重寫

Django

預設設定的主鍵。

主鍵字段是隻讀的,如果想修改一個模型類執行個體的主鍵并儲存,等同于建立一個新的模型類執行個體。例如:

from django.db import models


class Fruit(models.Model):
    name = models.CharField(max_length=100, primary_key=True)
           
>>> fruit = Fruit.objects.create(name='Apple')
>>> fruit.name = 'Pear'
>>> fruit.save()
>>> Fruit.objects.values_list('name', flat=True)
<QuerySet ['Apple', 'Pear']>
           

unique

如果設定為

True

,這個字段的值必須在整個表中保持唯一。

verbose_name

除了

ForeignKey

ManyToManyField

OneToOneField

,任何字段類型都接收一個可選的位置參數

verbose_name

,如果未指定該參數值,

Django

會自動使用字段的屬性名作為該參數值,并且把下劃線轉換為空格。

在該例中,備注名為

person's first name

:

在該例中,備注名為

first name

:

ForeignKey

ManyToManyField

OneToOneField

接收的第一個參數為模型的類名,後面可以添加一個

verbose_name

參數:

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

慣例是不将

verbose_name

的首字母大寫,必要時

Django

會自動把首字母轉換為大寫。

關聯

顯然,關系型資料庫的強大之處在于各表之間的關聯關系。

Django

提供了定義三種最常見的資料庫關聯關系的方法:多對一,多對多,一對一。

多對一關聯

定義一個多對一的關聯關系,使用

django.db.models.ForeignKey

類,和其他

Field

字段類型一樣,隻需要在模型類中添加一個值為關聯模型類的屬性。

ForeignKey

類需要添加一個位置參數,即想要關聯的模型類名。

例如,一個

Car

模型類有一個制造者

Manufacturer

,一個

Manufacturer

制造許多輛車,但是每輛車都僅有一個制造者,那麼使用下面的方法定義這個關系:

from django.db import models


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


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

建議設定

ForeignKey

字段名為想要關聯的模型類名,也可以自定義關聯字段名,例如:

class Car(models.Model):
    company_that_makes_it = models.ForeignKey(
        Manufacturer,
        on_delete=models.CASCADE,
    )
           
多對多關聯

定義一個多對多的關聯關系,使用

django.db.models.ForeignKey

類,和其他

Field

字段類型一樣,隻需要在模型類中添加一個值為關聯模型類的屬性。

ManyToManyField

類需要添加一個位置參數,即想要關聯的模型類名。

例如,如果

Pizza

含有多種

Topping

(配料),一種

Topping

可能存在于多個

Pizza

中,并且每個

Pizza

含有多種

Topping

,那麼可以表示這種關系:

from django.db import models


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

class Pizza(models.Model):
    # ...
    toppings = models.ManyToManyField(Topping)
           

建議設定

ManyToManyField

字段名為一個複數名詞(如

toppings

),表示所要關聯的模型類對象的集合。

注意:對于多對多關聯關系的兩個模型類,可以在任何一個模型類中添加

ManyToManyField

字段,但隻能選擇一個模型類設定該字段,即不能同時在兩模型類中添加該字段。一般情況下,應該把

ManyToManyField

執行個體放到需要在表單中被編輯的對象中。在之前的例子中,

toppings

被放在

Pizza

當中(而不是

Topping

中有指向

Pizza

ManyToManyField

執行個體),因為相較于配料被放在不同的披薩當中,披薩當中有很多種配料更加符合常理。按照先前說的,在編輯

Pizza

的表單時使用者可以選擇多種配料。

中介模型

如果隻是想要一個類似于記錄披薩和配料關系之間混合和搭配的多對多關系,标準的

ManyToManyField

就夠用了,但是有時可能需要将資料與兩個模型之間的關系相關聯。

舉例來講,考慮一個需要跟蹤音樂人屬于哪個音樂組的應用程式,在人和他們所在的組之間有一個多對多關系,可以使用

ManyToManyField

來表示這個關系。然而,如果想要記錄更多的資訊在這樣的關聯表當中,比如想要記錄某人是何時加入一個組的。對于這些情況,

Django

允許指定用于控制多對多關系的模型類:可以在中介模型當中添加額外的字段。在執行個體化

ManyToManyField

的時候使用

through

參數指定多對多關系使用哪個中介模型。舉例代碼如下:

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)
           

通過中介模型完成

ManyToManyField

後,可以通過執行個體化中介模型來建立多對多關系:

>>> ringo = Person.objects.create(name="Ringo Starr")
>>> paul = Person.objects.create(name="Paul McCartney")
>>> beatles = Group.objects.create(name="The Beatles")
>>> m1 = Membership(person=ringo, group=beatles, date_joined=date(1962, 8, 16), invite_reason="Needed a new drummer.")
>>> m1.save()
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>]>
>>> ringo.group_set.all()
<QuerySet [<Group: The Beatles>]>
>>> m2 = Membership.objects.create(person=paul, group=beatles, date_joined=date(1960, 8, 1), invite_reason="Wanted to from a band.")
>>> beatles.member.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>]>
           

如果自定義中介模型沒有強制

(model1, model2)

對的唯一性,調用

remove()

方法會将相關的執行個體都删除,調用

clear()

方法則會删除所有的中介模型執行個體。

>>> Membership.objects.create(person=ringo, group=beatles, date_joined=date(1968, 9, 4), invite_reason="You've been gone for a month and we miss you.")
>>> beatles.members.all()
<QuerySet [<Person: Ringo Starr>, <Person: Paul McCartney>, <Person: Ringo Starr>]>
>>> # This deletes both of the intermediate model instances for Ringo Starr
>>> beatles.members.remove(ringo)
>>> beatles.members.all()
<QuerySet [<Person: Paul McCartney>]>

>>> # Beatles have broken up
>>> beatles.members.clear()
>>> # Note that this deletes the intermediate model instances
>>> Membership.objects.all()
<QuerySet []>
           

一旦建立了自定義多對多關聯關系,就可以執行查詢操作,和一般的多對多關聯關系一樣,可以使用多對多關聯模型的屬性來查詢:

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
<QuerySet [<Group: The Beatles>]>

# Find all the members of The Beatles that joined after 1 Jan 1961
>>> Person.objects.filter(group__name='The Beatles', membership__date__joined__gt=date(1961,1,1))
<QuerySet [<Person: Ringo Starr]>

>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'

>>> ringos_membership = ringo.membership_set.get(group=beatles)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invite_reason
'Needed a new drummer.'
           
一對一關聯

使用

OneToOneField

來定義一對一關系,就像使用其他類型的

Field

一樣:在模型屬性中包含它。當一個對象以某種方式“繼承”另一個對象時,這對該對象的主鍵非常有用。

OneToOneField

需要一個位置參數:與模型相關聯的類。

例如,當建立一個有關“位置”資訊的資料庫時,可能會包含通常的位址,電話等字段。接着,如果想要接着建立一個關于“餐廳”的資料庫,除了将位置資料庫當中的字段複制到

Restaurant

模型類,也可以将一個指向

Place

模型類的

OneToOneField

放到

Restaurant

當中(因為“餐廳”是一個“地點”),事實上,在處理這樣的情況下最好使用模型繼承,它隐含的包括了一個一對一關系。和

ForeignKey

一樣,可以建立自關聯關系也可以建立與尚未定義的模型的關系。

OneToOneField

字段還可以接收一個可選的

parent_link

參數。

OneToOneField

類通常自動的成為模型的主鍵,這條規則現在不再使用了(然而可以通過手動指定

primary_key

參數)。是以,可以在單個模型當中指定多個

OneToOneField

字段。

字段命名限制

Django

對模型類的字段名有一些限制:

1.一個字段的名稱不能是

Python

保留字,因為這會導緻

Python

文法錯誤。比如:

class Example(models.Model):
    pass = models.IntegerField() # 'pass' is a reserved word!
           

2.一個字段名稱不能包含連續的多個下劃線,原因在于

Django

查詢文法的工作方式。比如:

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

3.字段名不能以下劃線結尾,原因同上。

SQL

保留字,例如

join

where

select

,是可以被用在模型字段名當中的,因為

Django

在對底層的

SQL

查詢當中清洗了所有的資料庫表名和字段名,通過使用特定資料庫引擎的引用文法。

Meta

選項

使用内部

Meta

類來給模型賦予中繼資料,就像:

from django.db import models


class 0x(models.Model):
    horn_length = models.IntegerField()
    
    class Meta:
        ordering = ["horn_length"]
        verbose_name_plural = "oxen"
           

模型的中繼資料即“所有不是字段的東西”,比如排序選項(

oedering

),資料庫表名(

db_table

),或是閱讀友好的單複數名(

verbose_name

verbose_name_plural

)。這些都不是必須的,并且在模型當中添加

Meta

類也是完全可選的。

模型屬性

objects

模型當中最重要的屬性是

Manager

,它預設是

Django

模型和資料庫查詢操作之間的接口,并且它被用作從資料庫中擷取執行個體對象,如果沒有指定自定義的

Manager

,預設名稱是

objects

Manager

隻能通過模型類來通路,不能通過模型執行個體來通路。

模型方法

在模型方法中添加自定義方法會給你的對象提供自定義的“行級”操作能力,與之對應的是類

Manager

的方法意在提供“表級”的操作,模型方法應該在某個對象執行個體上生效。這是一個将相關邏輯代碼放在一個地方的技巧——模型。比如,該模型有一些自定義方法:

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)
    
           

例子中的最後一個方法是

property

,下面介紹兩個最可能期望複寫的:

__str__()

一個

Python

的“魔法方法”,傳回值友好地展示了一個對象。

Python

Django

在要将模型執行個體展示為純文字時調用。最有可能的應用場景是互動式控制台或背景。

get_absolute_url()

該方法告訴

Django

如何計算一個對象的

URL

Django

在背景接口使用此方法,或任意時間它需要計算一個對象的

URL

。任何需要一個唯一

URL

的對象需要定義此方法。

模型繼承

模型繼承在

Django

中與普通類繼承在

Python

中的工作方式幾乎完全相同,但也應該遵循本頁開頭的内容。這意味着其基類應該繼承自

django.db.models.Model

你隻需要決定父類模型是否需要擁有它們的權利(擁有它們的資料表),或者父類僅作為承載僅子類中可見的公共資訊的載體。

Django

有三種可用的繼承風格。

  • 常見情況下,僅将父類用于子類公共資訊的載體,因為你不會想在每個子類中把這些代碼都敲代一遍。這樣的父類永遠都不會單獨使用,是以抽象基類是你需要的。
  • 若繼承了一個模型(可能來源于其它應用),且想要每個模型都有對應的資料表,請使用多表繼承。
  • 最後,若隻想修改模型的

    Python

    級行為,而不是以任何形式修改模型字段,代理模型是最佳選擇。
抽象基類

抽象基類在你要将公共資訊放入很多模型時會很有用。編寫你的基類,并在

Meta

類中填入

abstract=True

。該模型将不會建立任何資料表。當其用作其他模型類的基類時,它的字段會自動添加至子類。舉個例子:

from django.db import models


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

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

Student

模型擁有3個字段:

name

age

home_group

CommonInfo

模型不能用作普通的

Django

模型,因為它是一個抽象基類。它不會生成資料表,也沒有管理器,也不能被執行個體化和儲存。從抽象基類繼承來的字段可以被其他字段或值重寫,或用

None

删除。對很多使用者來說,這種繼承可能就是你想要的,它提供了一種在

Python

級抽出公共資訊的方法,但仍會在子類模型中建立資料表。

Meta繼承

當一個抽象基類被建立,

Django

将所有你在基類中申明的

Meta

内部類以屬性的形式提供。若子類未定義自己的

Meta

類,他将會繼承父類的

Meta

類,當然,子類也可直接繼承父類的

Meta

,比如:

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

在安裝

Meta

屬性前,對抽象基類的

Meta

做了一個調整——設定

abstract=False

。這意味着抽象基類的子類不會自動地變成抽象類。當然,你可以繼承一個抽象基類建立另一個抽象基類,隻需記住顯式地設定

abstract=True

。抽象基類的某些

Meta

屬性對子類是沒用的,比如包含

db_table

意味着所有的子類(你并未在子類中指定他們的

Meta

)會使用同一張資料表,這肯定不是我們想要的。

related_name

related_query_name

若你在外鍵或者多對多字段使用了

related_name

related_query_name

,你必須為該字段提供一個獨一無二的反向名字和查詢名字。這在抽象基類中一般會引發問題,因為基類中的字段都被子類繼承且保持同樣的值(包括

related_name

related_query_name

)。為了解決此問題,當你在抽象基類中(也隻能是在抽象基類中)使用了

related_name

related_query_name

,部分值需要包含

'%(app_lable)s'

'%(class)s'

  • '%(class)s'

    用使用了該字段的子類的小寫類名替換。
  • '%(app_label)s'

    用小寫的包含子類的應用名替換。每個安裝的應用名必須是唯一的,應用内的每個模型類名也必須是唯一的。是以,替換後的名字也是唯一的。、

舉個例子,有個應有

common/models.py

from django.db import models


class Base(models.Model):
    m2m = models.ManyToManyField(
        OtherModel,
        related_name="%(app_label)s_%(class)s_related",
        related_query_name="%(app_label)s_%(class)ss",
    )
    
    class Meta:
        abstract = True

class ChildA(Base):
    pass

class ChildB(Base):
    pass
           

附帶另一個應用

rare/models.py

from common.models import Base


class ChildB(Base):
    pass
           

common.ChildA.m2m

字段的反轉名是

common_childa_related

,反轉查詢名是

common_childas

common.ChildB.m2m

字段的反轉名是

common_childb_related

, 反轉查詢名是

common_childbs

rare.ChildB.m2m

字段的反轉名是

rare_childb_related

,反轉查詢名是

rare_childbs

。這決定于你如何使用

'%(class)s'

'%(app_label)s'

建構關聯名字和關聯查詢名。但是,若你忘了使用它們,

Django

會在你執行系統檢查(或運作

migrate

)時抛出錯誤。

如果你未指定抽象基類中的

related_name

) 屬性,預設的反轉名會是子類名,後接

'_set'

。這名字看起來就像你在子類中定義的一樣。比如,在上述代碼中,若省略了

related_name

) 屬性,

ChildA

m2m

字段的反轉名會是

childa_set

ChildB

的是

childb_set

多表繼承

Django

支援的第二種模型繼承方式是層次結構中的每個模型都是一個單獨的模型。某個模型都指向分離的資料表,且可被獨立查詢和建立。繼承關系介紹了子類和父類之間的連接配接(通過一個自動建立的

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=True)
           

Place

的所有字段均在

Restaurant

中可用,雖然資料分别存在不同的表中,是以,以下操作均可:

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

若有一個

Place

同時也是

Restaurant

,你可以通過小寫的模型名将

Place

對象轉為

Restaurant

對象。

>>> p = Place.objects.get(id=12)
# If p is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
           

然而,若上述例子中的

p

不是 一個

Restaurant

(它僅是個

Place

對象或是其它類的父類),指向

p.restaurant

會抛出一個

Restaurant.DoesNotExist

異常。

Restaurant

中自動建立的連接配接至

Place

OneToOneField

看起來像這樣:

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

你可以在

Restaurant

中重寫該字段,通過申明你自己的

OneToOneField

,并設定

parent_link=True

Meta

和多表繼承

多表繼承情況下,子類不會繼承父類的

Meta

。是以的

Meta

類選項已被應用至父類,在子類中再次應用會導緻行為沖突(與抽象基類中應用場景對比,這種情況下,基類并不存在)。故,子類模型無法通路父類的

Meta

類。不過,有限的幾種情況下:若子類未指定

ordering

屬性或

get_latest_by

屬性,子類會從父類繼承這些。

如果父類有排序,而你并不期望子類有排序,你可以顯示的禁止它:

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

繼承與反向關系

由于多表繼承使用隐式的

OneToOneField

連接配接子類和父類,是以直接從父類通路子類是可能的,就像上述例子展示的那樣。然而,使用的名字是

ForeignKey

ManyToManyField

關系的預設值。如果你在繼承父類模型的子類中添加了這些關聯,你必須指定

related_name

屬性。假如你忘了,

Django

會抛出一個合法性錯誤。

比如,讓我們用上面的

Place

類建立另一個子類,包含一個

ManyToManyField

:

class Supplier(Place):
    customers = models.ManyToManyField(Place)
           

這會導緻以下錯誤:

Reverse query name for 'Supplier.customers' clashes with reverse query
name for 'Supplier.place_ptr'.

HINT: Add or change a related_name argument to the definition for
'Supplier.customers' or 'Supplier.place_ptr'.
           

related_name

像下面這樣加至

customers

字段能解決此錯誤:

models.ManyToManyField(Place, related_name='provider')

代理模型

使用多表繼承時,每個子類模型都會建立一張新表。這一般是期望的行為,因為子類需要一個地方存儲基類中不存在的額外資料字段。不過,有時候你隻想修改模型的

Python

級行為——可能是修改預設管理器,或添加一個方法。

這是代理模型繼承的目的:為原模型建立一個代理。你可以建立、删除和更新代理模型的執行個體,所有的資料都會存儲的像你使用原模型(未代理的)一樣。不同點是你可以修改代理預設的模型排序和預設管理器,而不需要修改原模型。

代理模型就像普通模型一樣申明。你需要告訴

Django

這是一個代理模型,通過将

Meta

類的

proxy

屬性設定為

True

例如,假設你想為

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

類與父類

Person

操作同一張資料表。特别提醒,

Person

的執行個體能通過

MyPerson

通路,反之亦然。

>>> p = Person.objects.create(first_name="foobar")
>>> MyPerson.objects.get(first_name="foobar")
<MyPerson: foobar>
           

你也可以用代理模型定義模型的另一種不同的預設排序方法。你也許不期望總對

Persion

進行排序,但是在使用代理時,總是依據

'last_name'

屬性進行排序:

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

現在,普通的

Person

查詢結果不會被排序,但

OrderdPerson

查詢會按

last_name

排序。