版本:1.7

主要来源:https://docs.djangoproject.com/en/1.7/topics/db/models/

简单用法

from django.db import modelsclass Person(models.Model):first_name = models.CharField(max_length=30)last_name = models.CharField(max_length=30)

会自动生成SQL:

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

-默认会根据APP的名称生成"app名称"+"_"+"类名"
-会自动增加ID字段
-Django会根据settings配置中指定的数据库类型来生成相应的SQL语句

使用Model

要想使用Model需要在settings配置中INSTALLED_APPS中增加你的APP,例如,你的models在myapp.models中,那么就应该使用:

INSTALLED_APPS = (#...'myapp',#...
)

在将app添加到INSTALLD_APPS中后需要执行manage.py migrate,也可通过manage.py makemigrations进行迁移。

Fields

Django提供了各种数据字段类型。需要注意不要使用与API相冲突的字段名称如clean,save或delete

from django.db import modelsclass 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)name = models.CharField(max_length=100)release_date = models.DateField()num_stars = models.IntegerField()

Field types

django会根据field类型确定

  • 数据库字段类型(如INTEGER,VARCHAR)
  • 默认的HTML生成什么样的表单项
  • 最低限度的验证需求。它被用在 Django 管理后台和自动生成的表单中。

field是可以自定义的。

Field options

每个field类型都有自己特定的参数,但也有一些通用的参数,这些参数都是可选的:

null

如果为 True , Django 在数据库中会将空值(empty)存储为 NULL 。默认为 False 。

blank

设置字段是否可以为空,默认为False(不允许为空)

和null的区别在于:null是数据库的范围,而blank是用于验证。如果一个字段的 blank=True ,Django 在进行表单数据验证时,会允许该字段是空值。如果字段的 blank=False ,该字段就是必填的。

choices

它是一个可迭代的二元组(例如,列表或是元组),用来给字段提供选择项。如果设置了 choices, Django会显示选择框,而不是标准的文本框,而且这个选择框的选项就是 choices 中的元组。

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

每个元组中的第一个元素,是存储在数据库中的值;第二个元素是在管理界面或 ModelChoiceField 中用作显示的内容。在一个给定的 model 类的实例中,想得到某个 choices 字 段的显示值,就调用 get_FOO_display 方法(这里的 FOO 就是 choices 字段的名称 )。

from django.db import modelsclass 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
u'L'
>>> p.get_shirt_size_display()
u'Large'

default

默认值,可以是一个具体的值也可以是一个对象,每次调用次会创建一个新的对象

help_text

附加的帮助信息。在使用表单时会显示在字段下面。即使不使用表单也可以起来帮助文档的作用。

primary_key

如果为True,则表示这个字段是主键。

如果你没有设置主键,Django会自动创建一个自增的IntergerField类型主键,可以通过自定义主键来覆盖默认的行为。

unique

如果为 True ,那么字段值就必须是全表唯一的。


Automatic primary key fields

默认情况下,Django 会给每个 model 添加下面这个字段:

id = models.AutoField(primary_key=True)

这是一个自增主键字段。

如果你想指定一个自定义主键字段,只要在某个字段上指定 primary_key=True 即可。如果 Django 看到你显式地设置了 Field.primary_key,就不会自动添加 id 列。

每个 model 只要有一个字段指定 primary_key=True 就可以了。(可以自定义也可以保持默认自动增加的主键)


Verbose field names(详细名称)

每个字段的类型,除了ForeignKey, ManyToManyField 和 OneToOneField外,还有一个可选的第一位置参数,这个参数用于标记字段的详细名称。如果Verbose field names没有显示的给出,Django会自动创建这一字段属性,将字段名中的"_"转换成空格。

例如:设置详细名称为 "person's first name":

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

如果没有设置详细名称,则详细名称为: "first name":

first_name = models.CharField(max_length=30)

由于ForeignKey, ManyToManyField和 OneToOneField
需要使用第一参数,所以可以显示的使用 verbose_name来设置详细名称:

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

仅在以上特列中使用verbose_name,Django会自动利用第一个参数。

Relationships(关系)

Django支持关系数据库中常用的many-to-one,many-tomany,one-to-one

Many-to-one(多对一关系)

定义Many-to-one关系,使用django.db.models.ForeignKey。使用方法和其他字段类型一样:在model中定义时包含ForeignKey属性。

ForeignKey必须要一个参数:需要链接到哪个model.

例如:一辆汽车(car)和汽车制造商(manufacturer)的关系,那么一个汽车制造商会有制造多辆车,但每辆车却只能有一个汽车制造商:

from django.db import modelsclass Manufacturer(models.Model):# ...passclass Car(models.Model):manufacturer = models.ForeignKey(Manufacturer)

你也可以定义一个递归的关系(在对象内容实部Many-to-one的定义)和relationships to models not yet defined(没看明白,看完模型关系后再修改);

建议但不强制要求ForeignKey字段的名字是模型的小写字母的名字(例如在上例中使用的manufacturer)。当然你可以使用任何你想要的名字,例如:

class Car(models.Model):company_that_makes_it = models.ForeignKey(Manufacturer)

Many-to-many(多对多关系)

定义Many-to-many关系,使用django.db.models.ManyToManyField.使用方法和其他字段类型一样:在model中定义包含ManyToManyField属性。

ManyToManyField必须要一个参数:需要链接到那个model.

例如,一个Pizza有多个Topping对象——也就是一个Topping可以在多个Pizza上,每个Pizza有多个Toppings——这种情况我们可以这样定义:

from django.db import modelsclass Topping(models.Model):# ...passclass Pizza(models.Model):# ...toppings = models.ManyToManyField(Topping)

和ForeignKey一样,可以创建递归关系(在对象内部实现Many-to-many的定义)和relationships to models not yet defined

建议但不强制要求ManyToManyField的名字(上面的例子中的toppings)是复数形式,复数形式是为了描述相关模型对象的集合。

哪个模型带有ManyToManyField都没关系,但你只能在其中一个模型中使用它,而不能在两个模型中都使用它。

一般来说,ManyToManyField实例应该包含在使用表单编辑的对象中,在上面的例子中,toppings在Pizza中(而不是Topping有pizzas ManyToManyField),因为一个pizzas有多个Topping,比一个Topping在多个pizzas上更容易让人理解。这就是上面我们使用的方式,Pizza管理表单将让用户选择那种Topping。

还有一些可选参数。

Many-to-many关系的额外字段

如果只需要处理简单的多对多关系,就像上面pizzas和topping的关系,那么ManyToManyField字段就可以满足需要,然而,有些时候你需要让数据在两个模型之间产生联系。

例如,考虑一下跟踪乐队和乐队拥有的音乐家的应用程序的例子。这是一个人和这个人所在团队之间的多对多关系,因此你可以使用ManyToManyField来描述这个关系。然而,这种成员关系有很多你想要搜集的细节,比如这个人加入团队的时间。

对于这种情况,Django让你可以指定用来管理多对多关系的模型。然后你可以在中间模型中放入额外的字段。中间模型使用through参数指向像中间人一样工作的模型,来和ManyToManyField发生关系。对于四个音乐家的例子,代码可能像这样子:

from django.db import modelsclass Person(models.Model):name = models.CharField(max_length=128)def __str__(self):              # __unicode__ on Python 2return self.nameclass Group(models.Model):name = models.CharField(max_length=128)members = models.ManyToManyField(Person, through='Membership')def __str__(self):              # __unicode__ on Python 2return self.nameclass Membership(models.Model):person = models.ForeignKey(Person)group = models.ForeignKey(Group)date_joined = models.DateField()invite_reason = models.CharField(max_length=64)

当你建立中间模型时,你需要为模型明确地指定外键,以建立多对多关系。这个明确的声明定义了两个模型是如何产生联系的。

对于中间模型,有一些限制:

中间模型必须包含并且只包含一个到目标模型的外键(在上面的例子中的Group)。或者使用ManyToManyField.through_fields来明确指定外键关系。如果你有多个外键,但没有指定through_fields,会产生校验错误。类似的限制适用于外键的目标model(例如Person)

对于一个model通过中间model实现多对多关系,两个到同一模型的外键是允许的,但会被认为是多对多关系的两个不同侧面。如果有两个或以上的外键定义,你必须要定义through_fields,否则会产生校验错误。

当使用中间模型来定义一个到自己的多对多关系的模型时,你必须使用symmetrical=False(参阅“模型字段参考”)。

现在你已经建立了ManyToManyField来使用中间模型(在这个例子中是MemberShip),你可以开始创建一些多对多的对应关系了。具体来说是创建中间模型的一些实例:

现在已经在中间model中设置了ManyToManyField(例子中的Membership),你可以通过中间model创建关系实例:

>>> 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()
[<Person: Ringo Starr>]
>>> ringo.group_set.all()
[<Group: The Beatles>]
>>> m2 = Membership.objects.create(person=paul, group=beatles,
...     date_joined=date(1960, 8, 1),
...     invite_reason="Wanted to form a band.")
>>> beatles.members.all()
[<Person: Ringo Starr>, <Person: Paul McCartney>]

不同于一般的many-to-many关系字段,你不能通过直接通过关系对象进行增加、创建或赋值(即:beatles.members = [...])

# THIS WILL NOT WORK
>>> beatles.members.add(john)
# NEITHER WILL THIS
>>> beatles.members.create(name="George Harrison")
# AND NEITHER WILL THIS
>>> beatles.members = [john, paul, ringo, george]

这是因为你需要知道一些Person和Group关系之外的一些细节,这些细节在中间model--Membership中定义,而不仅仅只是简单创建了Person和Group之间的关系。类似关系的唯一解决办法是创建中间model

基于同样的原因 remove() 也是被禁用的,但可以通过 clear() 清除所有多对多关系实例:

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

一旦创建了中间model实例,并建立了一个多对多关系实例,就可以和正常的多对多关系一样进行查询:

# Find all the groups with a member whose name starts with 'Paul'
>>> Group.objects.filter(members__name__startswith='Paul')
[<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))
[<Person: Ringo Starr]

如果你需要访问membership’s 信息,你可以这样做直接查询Membership:

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

当然你也可以通过多对多的反向关系从Person 实例进行查询:

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

One-to-one关系

要定义一对一关系,请使用OneToOneField。它的使用方法和其它字段一样:把它包含在模型的类属性中。

当对象以某种方式扩展了另一个对象的主键时,对象的主键是最重要的。

OneToOneField要求一个位置参数:该模型相关的类。

例如,如果你将创建一个数据表places,你可能会在数据表中建立一些相当标准的东西,就像地址、电话号码等等。然后,如果你想在places上建立一个饭馆,你可以不必重复劳动,在Restaurant模型中复制这些字段,你可以建立一个带有到Place的OneToOneField(因为饭馆就是一个place;实际上,对于这种情况典型的做法是使用继承,这实际上是一种隐式的一对一关系)。

和外键一样,你可以定义循环关系和到未定义模型的关系;

OneToOneField也接受一个可选的参数parent_link 这个参数在“模型字段参考”有介绍。

OneToOneField类曾经会自动成为模型的主键。现在情况不再如此了(如果你愿意你可以手动传递一个primary_key参数)。因此,现在一个模型可以拥有多个OneToOneField类型的字段。

Models across files(跨文件的model)

在当前model和另一个应用程序中的model建立关系是没有问题的,只需要引入相关的model,然后在需要的时候使用就可以了:

from django.db import models
from geography.models import ZipCodeclass Restaurant(models.Model):# ...zip_code = models.ForeignKey(ZipCode)

Field name restrictions(字段名限制 )

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!

这些限制是可以绕过的,因为你的字段名并不需要和数据表的列名匹配。

SQL保留字,比如join、where或select,可以用作模型字段名,因为Django在进行底层的SQL查询前会对所有数据表名和列名进行转义。

Custom field types(自定义字段类型)

如果现有的字段类型不能满足你的需要,或者你使用的数据库具有一些特殊的类型,你可以创建自己字段类型。

Meta options

定义model的metadata(元数据)是通过使用一个内部类Meta,例:

from django.db import modelsclass Ox(models.Model):horn_length = models.IntegerField()class Meta:ordering = ["horn_length"]verbose_name_plural = "oxen"

model元数据不是一个字段,例如ordering,db_table,verbose_name 和 verbose_name_plural这些附加选项,都可以放到class Meta中,这些选项都不是必需的。

Model attributes

objects

这是model最重要的Manager属性,通过它查询数据库并于model之间形成映射。如果没有自定义manager,默认名称为objects。managers只能通过model类,而不能通过model类实例来访问。

Model methods

通过model自定义方法添加自定义"行级"功能,而managers方法是为“表级”添加自定义功能,model自定义方法可以通过model实例来使用。

这样就可以把业务逻辑都放在一个model里。例如:

from django.db import modelsclass 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 datetimeif 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"def _get_full_name(self):"Returns the person's full name."return '%s %s' % (self.first_name, self.last_name)full_name = property(_get_full_name)

上个实例的最后一个方法是属性。

这个model实例继承自models.Model,会自动具备大量的方法,可以覆盖大部分的方法,但有几个却是必须的:

str() (Python 3)

在Python3中相当于unicode()

unicode() (Python 2)

这是一个Python的"魔术方法",它以unicode方式返回任何对象的陈述。Python和Django需要输出字符串陈述时使用。例如在交互式控制台或管理后台显示的输出陈述。

默认的实现并不能很好的满足需要,所以最好自定义这个方法,

get_absolute_url()

定义对象的URL.在管理界面中,任何时候都可以通过URL找到一个对象。

任何通过URL访问的对象,都应该有唯一标识。

Overriding predefined model methods

还有一些model方法封装了一些行为。当你想自定义数据库行为,尤其是想改变save() 和 delete()方式的时候。

你可以自由地重写这些方法来改变行为。

如果你想在保存对象的时候,覆盖内置save() 行为:

from django.db import modelsclass Blog(models.Model):name = models.CharField(max_length=100)tagline = models.TextField()def save(self, *args, **kwargs):do_something()super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.do_something_else()

你也可以阻止保存:

from django.db import modelsclass Blog(models.Model):name = models.CharField(max_length=100)tagline = models.TextField()def save(self, *args, **kwargs):if self.name == "Yoko Ono's blog":return # Yoko shall never have her own blog!else:super(Blog, self).save(*args, **kwargs) # Call the "real" save() method.

需要特别注意的要记得调用父类的方法--super(Blog, self).save(args, *kwargs),以确保对象仍然被保存到数据库中,如果你忘记调用父类的方法,默认的行为不会发生,数据库也不会发生改变。

随着时间的推移,django会增加或扩展一些新的方法,如果你使用args, *kwargs作为你的方法参数,就必须要保证能正确处理这些参数的增加。

覆盖方法大多数不会用于批量操作

delete()并不一定是在调用一个QuerySet批量删除时被触发。为了确保自定义的删除逻辑被执行,则需要使用 pre_delete and/or post_delete 信号。

不幸的是,还没有一个好的方法用于批量的创建和更新,因为没有save()、pre_save、post_save会被调用。

Executing custom SQL

另外一种常见的模式是在model方法和module-level方法中执行自定义SQL。

Model inheritance(Model继承)

Model的继承和普通的Python类继承几乎相同,但基类的继承必须是django.db.models.Model.

在Django中有三种继承风格:

1、如果你想用父类来保存每个子类共有的信息,并且这个类是不会被独立使用的,那么应该使用抽象基类。

2、如果你继承现有model(甚至可能这个类是在另一个应用程序中),并希望每个model都拥有自己对应的数据库,那就应该使用多表继承。

3、如果你只是想修改model的Python-level行为,而不改变models fields,则使用代理模式。

Abstract base classes

当你想集中一些公共信息,可以使用虚类。你需要在model的元数据中设置 abstract=True ,这个model将不会被用于创建任何数据表。相反,当他被作为其他子类的基类时,他的字段将被添加到这些子类中。如果抽象类和他的子类有相同名称的字段会产生一个异常。

例如:

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)

Student会拥有三个字段:name\age\home_group。CommonInfo将不能像普通的Django model一样使用,因为他是一个抽象基类。他不会产生一个数据表或者拥有一个管理器,也不能被实例化或直接调用。

在许多情况下这种类型的继承正是你想要的,它提供了一种用来分解公共信息的方法,虽然只能实现数据表级别创建子模型。

Meta inheritance

当创建一个抽象基类的时候,Django允许在基类中声明各种Meta属性,如果子类没有声明自己的Meta元数据,他将继承父类的。如果子类想扩展父类的Meta元数据,则可以继承父类的Meta。例如:

from django.db import modelsclass CommonInfo(models.Model):# ...class Meta:abstract = Trueordering = ['name']class Student(CommonInfo):# ...class Meta(CommonInfo.Meta):db_table = 'student_info'

Django对于抽象基类的元数据调整只应该在安装之前设置abstract=false。这意味着从抽象基类派生的子类不会自动转型成抽象类本身。当然你也可以继承来自别一个抽象基类。只需要记住abstract=True每次都应该明确设置。(这段没怎么看明白)

在抽象基类中某些属性几乎是没有任何意义的,包括父类的元数据。例如使用db_table将意味着所有的子类(那些没有指定自己的Meta)将使用同一数据库,这肯定不会是你想要的。

Be careful with related_name(注意抽象基类中的反向关系名称定义)

如果你在ForeignKey和ManyToManyField的属性中使用related_name,你必须为字段指定一个唯一反向关系名称。在抽象基类中会有一些问题,因为抽象基类的每个字段都会被包括在他的每一个子类中,包括related_name.

要解决这个问题,应该在抽象基类中使用related_name时,名称中应包含'%(app_label)s' 和 '%(class)s'.

'%(class)s':小写的子类名称
'%(app_label)s':应用的小写名称(app)。因为每个已安装的应用名称都是唯一的,所以产生的名称最终也会不同。

例如:首先定义common/models.py:

from django.db import modelsclass Base(models.Model):m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")class Meta:abstract = Trueclass ChildA(Base):passclass ChildB(Base):pass

然后另一个APP rare/models.py:

from common.models import Baseclass ChildB(Base):pass

在这个示例中 common.ChildA.m2m 字段的反向关系名称是common_childa_related,而common.ChildB.m2m 的关系名称ommon_childb_related。这是因为使用了'%(app_label)s' 和 '%(class)s'产生了不同的反向关系名称。如果你定义了related_name,但忘记了使用'%(app_label)s' 和 '%(class)s' Django会系统检查或运行migrate时引发错误。

如果没有在抽象基类的字段中定义related_name属性,默认关系名称将是子类名称+"_set"。通常related_name会被直接在子类的字段属性中被定义。例如上例中,如果related_name属性被省略。common.ChildA.m2m 的反向关系名称应该是childa_set,common.ChildB.m2m的反向关系名称应该是childb_set。

Multi-table inheritance(多表继承)

Django支持的第二种model继承是多表继承,在继承结构中每个model都是独立的。都对应着自己的数据库表,可以进行独立的查询等操作。继承关系实际是子model和每个父model之间的关系(通过自动创建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)

所有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: ...>

但如果place对象所对应的Restaurant对象不存在,则会引发 Restaurant.DoesNotExist 异常。

Meta and multi-table inheritance

在多表继承的情况下继承父类的Meta是没有意义的。所有的Meta都已经被应用到父类,再应用这些Meta只会导致矛盾。

所以子model不能访问到父model的Meta,然而也有少数的情况下,子model会从父model中继承一些行为,例如子model没有指定 ordering或 get_latest_by属性,那么就会从父model中继承。

如果父model中有一个排序,但你不希望子model有任何的排序规划,你可以明确的禁用:

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

Inheritance and reverse relations(继承与反向关系)

因为多表继承实际是隐式的使用OneToOneField来键接父Model和子model,在这种关系有可能会使用父model来调用子model,比如上面的例子。但是如果你把ForeignKey和ManyToManyField关系应用到这样一个继承关系中,Django会返回一个验证错误,必须要指定一个related_name字段属性。

例如上面的例子,我们再创建一个子类,其中包含一个到父model的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'.

解决这个问题只需要在customers字段属性中增加related_name属性:models.ManyToManyField(Place, related_name='provider').

Specifying the parent link field

如上所述,Django会自动创建一个OneToOneField链接你的子model和任何非抽象父model。如果你想自定义子model键接回父model的属性名称,你可以创建自己的OneToOneField并设置parent_link=True,表示这个字段是对父model的回链。

Proxy models

当使用多表继承时一个新的数据表model会在每一个子类中创建,这是因为子model需要存储父mdoel不存在的一些数据字段。但有时只需要改变model的操作行为,可能是为了改变默认的管理行为或添加新的方法。

这时就应该使用代理模式的继承:创建原始model的代理。你可以创建一个用于 create, delete 和 update的代理model,使用代理model的时候数据将会真实保存。这和使用原始model是一样的,所不同的是当你改变model操作时,不需要去更改原始的model。

代理模式的声明和正常的继承声明方式一样。你只需要在Meta class 中定义proxy为True就可以了。

例如,你想为Person model添加一个方法:

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

MyPerson这个类将作用于父类Person所对应的真实数据表。可通过MyPerson进行所有相应的操作:

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

你也可以使用代理模式来定义model的不同默认排序,例如:

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

这样当使用原始model查询时结果是无序的,而使用OrderedPerson进行查询时将按last_name进行排序。

QuerySets still return the model that was requested(QuerySets的类型依然会是原始model类型)

当你通过MyPerson来查询Person对象时,返回的QuerySet依然会是Person对象类型的集合。使用代理模式的model是依靠原始model的,是原始model的扩展。而不是用来替代父model。

Base class restrictions(基类限制)

代理model必须继承一个非抽像model。

不能从多个非抽像model继承,代理模式不能为不同model之间创建链接。

代理模式可以从任意没有定义字段的抽象model继承。

Proxy model managers

如果没有指定代理model的管理器(managers),它将继承父类的管理行为。如果你定义了代理model的管理器,它将会成为默认的,当然父类中定义的定义的任何管理器仍然是可以使用的。

继续上面的例了,增加一个默认的管理器:

from django.db import modelsclass NewManager(models.Manager):# ...passclass MyPerson(Person):objects = NewManager()class Meta:proxy = True

可以通过创建一个含有新的管理器并进行继承,来增加一个新的管理器,而不需要去改变更有的默认管理器。

# Create an abstract class for the new manager.
class ExtraManagers(models.Model):secondary = NewManager()class Meta:abstract = Trueclass MyPerson(Person, ExtraManagers):class Meta:proxy = True

Differences between proxy inheritance and unmanaged models

代理model看起来很像一个在Meta class中设置了manged的非托管模式model。但实际上这两种方案是不太一样,应该考虑在不同的情况下使用那一个:

两者区别在于:你可以设置model的Meta.managed=False以及通过Meta.db_table指定数据表有创建非托管模式model,并对其添加各种方法,但如果你可保持非托管模式和真实数据表之间的同步,做任何更改都将是很麻烦的事。

而代理model主要用于管理model的各种行为或方法,他们将继承父model的管理器等。

曾经尝试将两种模式合并,但由于API会变得非常复杂,并且难以理解,所以现在是分离成两种模式:

一般的使用规划是:

1、如果正使用现有的数据表,但不想在Django中镜像所有的列,所以应该使用Meta.managed=False,通过这个选项使不在django控制下的数据表或视图是可用的。

2、如果你想改变一个model的操作行为,但希望保持原始model不被改变,就应该使用Meta.proxy=True.

Multiple inheritance(多重继承)

和Python的继承方式一样,django中的model也可以从多个父model继承,当然也和Python的继承方式 一样,如果出现相同名字的时候只有第一个将被使用。例如:如果多个父model中都包含Meta类,将只有第一个将被使用,其他会被忽略。

通常情况下是不会用到多重继承的。主是用于“混合式”model:增加一个特殊的额外字段或方式是由多个父model组合而来。应该尽量保持继承层次的简单,不然会很难排查某个信息是从那里来的。

在django1.7以前,多个父model中有id主键字段时虽然不会引发错误,但有可能导致数据的丢失。例如像下面的model:

class Article(models.Model):headline = models.CharField(max_length=50)body = models.TextField()class Book(models.Model):title = models.CharField(max_length=50)class BookReview(Book, Article):pass

下面的使用方法演示了怎么用一个子对象来覆盖父对象的值:

>>> article = Article.objects.create(headline='Some piece of news.')
>>> review = BookReview.objects.create(
...     headline='Review of Little Red Riding Hood.',
...     title='Little Red Riding Hood')
>>>
>>> assert Article.objects.get(pk=article.pk).headline == article.headline
Traceback (most recent call last):File "<console>", line 1, in <module>
AssertionError
>>> # the "Some piece of news." headline has been overwritten.
>>> Article.objects.get(pk=article.pk).headline
'Review of Little Red Riding Hood.'

要正确的使用多重继承,你应该使用一个明确的 AutoField 在父model中:

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

或者使用一个共同的父类来定义AutoField:

class Piece(models.Model):passclass Article(Piece):...class Book(Piece):...class BookReview(Book, Article):pass

Field name “hiding” is not permitted

正常的Python类继承,允许一个子类覆盖父类的任何属性。在Django中是不允许覆盖父类的属性字段的。如果一个父类中定义了一个叫author的字段,你就不能在子model中创建别一个叫author的字段。

这种限制仅适用于字段(field),普通的python属性是可以的。也有一种情况是可以覆盖的:多表继承的时候进行手动指定数据库列名,可以出现子model和父model有同名的字段名称,因为他们属于不同的数据表。

如果你覆盖了父model的字段属性,django会抛出FieldError异常。

文/罗田(简书作者)
原文链接:http://www.jianshu.com/p/c10be59aad7a
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。

Django Model 定义语法相关推荐

  1. python django model定义

  2. Python自动化运维:Django Model进阶

    QuerySet 可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. >>> Entry.objects.all()[ ...

  3. Django model进阶

    QuerySet 1.可切片 使用Python 的切片语法来限制查询集记录的数目 .它等同于SQL 的LIMIT 和OFFSET 子句. >>> Entry.objects.all( ...

  4. Django——Model

    一. ORM 在 MVC 或者说 MTV 设计模式中,模型(M)代表对数据库的操作.那么如何操作数据库呢? 我们可以在 Python 代码中嵌入 SQL 语句. 但是问题又来了,Python 怎么连接 ...

  5. Django Model View Template 之间的简单交互 (二)

    前言 接续前文,上一篇文章主要涉及了 Django 项目的基础配置等,这篇主要涉及数据库相关的 ORM ,也就是 Django 中的 Model 的使用,MVT 三层之间的交互 教程基本都是东拼西凑的 ...

  6. django model

    本文一篇完全介绍django的最重要的model 6.django model 模型是数据唯一而且准确的信息来源.它包含正在储存的数据的重要字段和行为.一般来说,每一个模型都映射一个数据库表. 这个模 ...

  7. Django model 中的 class Meta 详解

    通过一个内嵌类 "class Meta" 给你的 model 定义元数据, 类似下面这样: 1 2 3 4 5 class Foo(models.Model):     bar = ...

  8. django model数据 时间格式

    from datetime import datetime dt = datetime.now() print '时间:(%Y-%m-%d %H:%M:%S %f): ' , dt.strftime( ...

  9. django model Meta选项

    可用的 Meta 选项 abstract Options.abstract 如果 abstract = True ,这个 model 就是一个 抽象基类 . app_label Options.app ...

最新文章

  1. 马尔可夫模型与条件随机场模型
  2. 用计算机怎么开启音乐模式,XP开机音乐怎么设置?如何设置电脑开机音乐?
  3. 8个最好的Linux平台商业智能(BI)软件
  4. Cpp / checked_delete 原理
  5. Json.NET Deserialize时如何忽略$id等特殊属性
  6. 更换Spring底层日志框架
  7. 8皇后以及N皇后算法探究,回溯算法的JAVA实现,递归方案
  8. python一维列表的定义_数据结构-Python 列表(List)
  9. jpa root.join_JPA 2.1和Java EE 7中的JPQL增强功能(第1部分– JOIN ON)
  10. Django中提供了6种缓存方式
  11. html5调用系统声音1s响一次_20款奔驰GLC260提车改柏林之声音响,音乐诉请,为爱发声!...
  12. 图解TCP三次握手和四次挥手!(简单易懂)
  13. filewriter判断是否关闭_IO流详解
  14. 在PostgreSQL中解码Django会话
  15. C#中的表达式与运算符
  16. 用DirectX Audio和DirectShow播放声音和音乐(3)
  17. 22 C#中的异常处理入门 try catch throw
  18. JS对象 - Array属性方法汇总
  19. 高数复习(1)--曲线切向与曲面法向的理解
  20. yara规则--构建yara规则库

热门文章

  1. TP5 在西部数码虚拟主机下 要如何实现伪静态
  2. Nginx Web 基础入门
  3. 腾讯、阿里、百度高工都点头称赞的“Redis 实战超全笔记”,不看你就亏大发了
  4. 神奇的主力成本线!居然能将主力成本运筹帷幄?!
  5. godot mysql_Go 每日一库之 godotenv
  6. 85. 如何用 OPA5 编写测试用例来测试用户输入文本的功能
  7. 计算机应用专业需要6g显卡吗,4G、6G、8G显卡的显存容量有什么用?来看科普
  8. Photoshop-液化工具对人物的应用
  9. linux中获取几天前或者几天后的日期
  10. HDU杭电OJ经典100题2000-2099_Java版详细题解(持续更新)