天天看点

《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

排序。