此文翻译自django 1.8.2官方文档
模型是单一的,明确的和你的数据有关的信息的来源.它包含了你存储的数据的基本字段和行为.通常,一个模型对应一个数据库表.
基础知识:
- 每个模型都是django.db.model.Model的子类
- 模型的每个属性都代表一个数据库字段
- 在这2个前提下,django提供了自动生成(automatically-generated)数据库连接(database-access)的API;
这个示例模型定义了一个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)
first_name和last_name是模型的字段.一个字段就是一个类属性,每个属性都映射一个数据库的列.
上面的Person模型会像这样创建一个数据库:
CREATE TABLE myapp_person(
"id" serial NOT NULL PRIMARY KEY,
"first_name" varchar(30) NOT NULL,
"last_name" varchar(30) NOT NULL
)
一些技术要点:
- 表的名字,myapp_person,是由模型的元数据(metaclass)自动生成的,但可以重写.
- id字段是自动添加的,这个行为(behavior)也可以重写.
- 这段建表SQL语句用的是PostgreSQL语法格式化的.值得注意的是,django根据settings文件里指定的数据库驱动来使用SQL.
在你定义了模型之后,要告诉django你将要使用这些模型.编辑你的settings文件,更改INSTALLED_APPS设置,添加包含你的models.py的模块名.
INSTALLED_APPS = {
# ...
'myapp',
# ...
}
当你添加新app到INSTALLED_APPS后,确保运行manage.py migrate,也可以先运行manage.py makemigrations.
模型最重要的部分-并且是唯一必需的部分-是它定义的数据库字段.这些字段由类属性指定.小心选择字段名,不要和模型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)
name = models.CharField(max_length=100)
release_date = models.DateField()
num_stars = models.IntegerField()
模型里的每个字段都是一个合适的Field类的实例.django使用field class类型来决定一些事情:
- 数据库字段类型(例如 INTEGER, VARCHAR).
- 渲染表单字段时,默认使用的HTML widget(例如,
django有很多内置的字段类型;如果内置的不够用,你可以很容易的定制你自己的字段.
每个字段都有一些指定字段专用(field-specific)的参数.例如,CharField(和它的子类)需要max_length参数来指定数据库里用来存储数据的VARCHAR的长度.
同样有些通用参数适用于所有字段类型.这些都是可选的.这里是最常用的参数的摘要:
- null
如果是True,django会将空数据当作NULL存入数据库.默认是False.
- blank
如果是True,字段允许是空值(blank).默认是False.
注意这和null不同.null是纯数据库相关的(pure database-related),但是blank是验证相关的(validation-related).如果字段有blank=True,表单验证会允许输入空字符.如果字段是blank=Flase,字段值不能是空字符.
- choices
由2元素元组(2-tuples)组成的可迭代对象,作为字段的choices.如果有这个属性,默认的表单widget就是一个下拉选框而不是标准的文本字段,并限制选择给出的选项.
一个choices list 就像这样:
YEAR_IN_SCHOOL_CHOICES = (
('FR', 'Freshman'),
('SO', 'Sophomore'),
('JR', 'Junior'),
('SR', 'Senior'),
('GR', 'Graduate'),
)
每个元组的第一个元素是要存到数据库的值,第二个元素是默认显示在表单widget里或在ModelChoiceField里.给定一个模型对象的实例,choices字段的显示值可以通过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'
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)
['Apple', 'Pear']
再次说明,这些只是最常用的字段可选参数的简短描述,全部详情请查看参考文档.
默认情况下,django会给每个模型一个字段:
id = models.AutoField(primary_key=True)
这是一个自增长主键.
如果你想指定一个自定义的主键,只需要再其中一个字段中指定primary_key=True就行了.如果django看到你显式的设置了Field.primary_key,就不会自动添加id字段了.
每个模型都必须要有一个字段是primary_key=True(显示声明或者自动添加)
每个字段类型,除了ForeignKey,ManyToManyField和OneToOneField,都有一个可选的第一位置的参数-别名(verbose name).如果没有给定别名,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会自动将首字母改成大写.
显然,关系型数据库的强大在于表关联.django提供提供了定义3中常用的数据库关联关系的方法:多对一,多对多,一对一.
- Many-to-one relationships
使用django.db.models.ForeignKey来定义多对一关系.使用起来就像其他字段类型一样:就像你的模型的类属性一样包含它就行了.
ForeignKey需要一个位置参数:模型的关联类.
例如,如果一个Car模型有一个Manufacturer-就是说一个Manufacturer制造多个汽车,但是每个Car只能由一个Manufacturer-使用下面的定义:
from django.db import models
class Manufacturer(models.Model):
# ...
pass
class Car(models.Model):
manufacturer = models.ForergnKey(Manufacturer)
# ...
You can also create recursive relationships (an object with a many-to-one relationship to itself) and relationships to models not yet defined; see the model field reference for details.
建议,但不强制要求,ForeignKey字段(上面的例子是manufacturer)的名字是模型名的小写.当然,你也可以自己取名.例如:
class Car(models.Model):
company_that_makes_it = models.ForeignKey(Manufacturer)
from django.db import models
class Topping(models.Model):
# ...
pass
class Pizza(models.Model):
# ...
toppings = models.ManyToManyField(Topping)
和ForeignKey一样, you can also create recursive relationships (an object with a many-to-many relationship to itself) and relationships to models not yet defined; see the model field reference for details.
建议但不强制要求,ManyToManyField的名字(这个例子中是toppings)用关联对象的复数形式.
无论是哪个模型用ManyToManyField都可以,只要其中一个有就行了,不是2个都要有.
通常情况下,ManyToManyField实例放到需要在表单里编辑的对象里.在上面的例子中,toppings在Pizza里(而不是Topping有一个pizzas ManyToManyField)因为一个披萨用多种调料比一种调料可以用于多种披萨更符合自然思维.在上面的的设置中,Pizza表单中用户可以选择多个topping.
ManyToManyField有许多可选参数,详情请看参考文档.
- Extra fields on many-to-many relationships
当你处理简单的多对多关系就像混合,匹配披萨和调料时,标准的ManyToManyField就是你需要的.但是有时候你可能需要处理的数据和2个模型都有关联.例如,假设有一个应用要记录乐手属于哪个组合.这里就有个多对多关系,一个人和这个人参加过的组合,所以你可以使用ManyToManyField来表现这种关系.但是有很多关于成员的细节你要考虑到,比如乐手加入组合的时间.
为了应付这种情况,django允许你定制模型用于多对多关系.你可以在中介模型(intermediate model)里加入额外的字段(extra field).中介模型使用through参数来关联到ManyToManyField,从而指明模型是中介模型(act as an intermediary).就用musician的例子,代码就像下面这样:
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=128)
def __str__(self): # python2使用__unicode__
return self.name
class Group(models.Model):
name = models.CharField(max_length=128)
members = models.ManyToManyField(Person, through='Membership')
def __str__(self): # python2使用__unicode__
return self.name
class Membership(models.Model):
person = models.ForeignKey(Person)
group = models.ForeignKey(Group)
date_joined = models.DateField()
invite_reason = models.CharField(max_length=64)
使用中介模型时,显式指定多对多关系模型的外键.这个显式声明定义了2个模型是怎么关联到一起的.
使用中介模型有一些限制:
中介模型必须包含有且只有一个源模型(source model)(这里的例子中是Group)的外键,否则你必须使用ManyToManyField.through_fields来显式指定外键.如果你有多个外键并且through_fields没有指定,会抛出一个validation error.对于目标模型(target model)(这里的例子中是Person),这个规则也适用.
对于一个使用中介模型的有多对多关系的模型来说,允许有2个外键指向同一个模型,但是他们必须当作2个不同的多对多关系处理.If there are more than two foreign keys though, you must also specify through_fields as above, or a validation error will be raised.
When defining a many-to-many relationship from a model to itself, using an intermediary model, you must use symmetrical=False
django1.7更改:
在django1.6及更早版本中,多对多关系的模型每个模型只能有一个外键在中介模型里.
现在你可以像这样来使用
>>> 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()
[]
>>> ringo.group_set.all()
[]
>>> m2 = Membership.objects.create(person=paul, group=beatles, date_joined=date(1960, 8, 1), invite_reason='Wanted to form a band.')
>>> beatles.members.all()
[,]
和普通的多对多字段不同,你不能使用add,create或者赋值语句(例如,beatles.members=[…])来创建关联:
# 这个不会起作用
>>> beatles.memebers.add(john)
# 这个也是
>>> beatles.members.create(name='George Harrison')
# 这个也是
>>> beatles.members = [john, paul, ringo, george]
为什么呢,你不能只是新建一个Person和Group的关联-你要指定所有在Membership模型中需要的详情.简单的add,create和赋值调用没有提供一个指定额外细节的方法.
所以,他们对使用中介模型的多对多关系不起作用.唯一的途径是新建中介模型的实例.
remove()方法不起作用,也是这个原因.但是clear()方法可以移除一个实例的所有多对多关系:
>>> # 披头士解散
>>> beatles.members.clear()
>>> # 这将删除中介模型实例
>>> Membership.objects.all()
[]
在通过创建中介模型实例来确立了多对多关系后,你可以开始使用查询了.就像普通的多对多关系一样,你能使用多对多关系模型的属性来查询:
# 找出有成员名字以'Paul'开头的组
>>> Group.objects.filter(members__name__startwith='Paul')
[]
如果你使用了中介模型,你也可以用他的属性来查询:
# 找出披头士在1 Jan 1961后加入的成员
>>> Person.objects.filter(group__name='The Beatles', membership__date__joined__gt=date(1961,1,1))
[]
如果你要查看membership的信息,你可以直接查询Membership模型:
>>> ringos_membership = Membership.objects.get(group=beatles, person=ringo)
>>> ringos_membership.date_joined
datetime.date(1962, 8, 16)
>>> ringos_membership.invit_reason
'Needed a new drummer.'
另一个方法是通过Person对象来查询(多对多反向关系)many-to-many reverse relationship:
>>> 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.'
模型可以跨应用关联.为此,需要在定义模型的文件头部import关联模型.然后,在需要的地方关联其他模型就可以了.例如:
from django.db import models
from geography.models import ZipCode
class Restaurant(models.Model):
# ...
zip_code = model.ForeignKey(ZipCode)
django只有2个模型字段名限制:
1. 字段名不能是python关键字,因为这会造成python语法错误.例如:
class Example(models.Model):
pass = models.IntegerField() # 'pass'是关键字!
class Example(models.Model):
foo__bar = models.IntegerField() # 'foo__bar'有2个下划线!
这些限制是可以规避的,因为你的字段名没有必要一定和和数据库字段名相同.详情见db_column可选项.
SQL关键字,例如join,where或select允许作为模型字段名,因为在每个SQL查询中django都对表名和字段名进行了转义.它使用特定的数据库引擎的引号语法.
如果现有的模型字段都不适用或者你想使用一些非常用数据库字段类型,你可以创建你自己的字段类.详情请查看参考文档.
使用内部类来给模型添加元数据(metaclass),就像下面的:
from django.db import models
class Ox(models.Model):
horn_length = models.IntegerField()
class Meta:
ordering = ['horn_length']
verbose_name_plural = 'oxen'
模型元数据是”不是字段的其他任何东西”(anything that’s not a field),比如说ordering选项(ordering),数据库表名(db_table)或者人类可读的(human-readable)单数和复数形式(verbose_name和verbose_name_plural).Meta类是可选的,非必需.
详情请看参考文档.
objects
模型最重要的属性是Manager.它是提供给django模型的数据库查询接口,用于从数据库中查询实例.如果没有自定义Manager,默认的名称是objects.Manager只能通过模型类来访问,而不是模型实例.
通过定义模型方法可以给对象添加”row-level”functionality.不同于Manager方法用于整个表(intended to do ‘table-wide’ things),模型方法可以作用于单个模型实例.
这个技巧将业务逻辑保存到模型里.
例如,这个模型有几个自定义方法:
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):
"Return 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"
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)
例子最后一个方法是property
每个模型都会自动添加一些方法,大部分都可以重写,详情请看参考文档.有几个是常用的:
__str__() (python3)
python3中等价于__unicode__()
__unicode__() (python2)
python的’magic method’,返回一个对象的unicode表示.当一个模型实例需要被显示为普通字符串时python和django会使用这个方法.最常见的,当你使用交互命令行或admin显示一个对象时就会用到.
最好定义这个方法,默认的很不好用.
get_absolute_url()
这表示django怎么为一个对象计算URL.在admin接口或其他任何需要计算对象的URL的时候django会用到这个方法.
任何有唯一标识URL的对象应该定义这个方法.
还有一组模型方法,封装了一些数据库动作,你可以自定义.特别是save()和delete()方法,经常改写.
你可以任意改写这些方法(或其他任何模型方法)来改变数据库行为.
一个重写内置方法的典型案例是当你保存一个对象时想做些什么.例如:
from django.db import models
class 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) # 调用"真的"save()方法
do_something()
你也可以阻止保存
from django.db import models
class Blog(models.Model):
name = models.CharField(max_length=100)
tagline = models.TextField()
def save(self, *args, **kwargs):
if self.name == "YoYo Ono's blog":
return # Yoko不能有自己的blog!
else:
super(Blog, self).save(*args, **kwargs)
一定要记得调用父类的方法-super(Blog, self).save(*args, **kwargs)-确保对象保存到数据库里.如果你忘记调用父类的方法,默认的行为将不会发生,数据库不会有变化. 另一点非常重要的是你传递了参数到模型方法*args, **args.django会时不时的添加新参数来扩展内置方法.如果你在方法定义里使用了*args和**kwargs,就能确保当有新参数添加时你的代码能自动支持这些参数.
Overridden model methods are not called on bulk operatins
注意,当使用QuerySet删除大量对象时,delete()方法不一定会被调用.为了确保自定义的删除逻辑会被执行,你可以使用pre_delete()或post_delete()信号.
不幸的是,当大量create或update对象时没有变通方法.Unfortunately, there isn’t a workaround when creating or updating objects in bulk, since none of save(), pre_save, and post_save are called.
另外一个常用的模式是在模型方法和模块级方法里写自定义的SQL语句.详情请看参考文档.
django的模型继承的工作方式和正常的Python类继承几乎一样,但是还有有些基本原则要遵守的.基类一定是django.db.models.Model.
你唯一要做的决定是父模型是否有属于自己的模型的权利(有自己的数据库表),或者说父类只保留公共信息,只有通过子模型才有可见性(only be visible throuth the child models).
django有3中继承方式.
1. 通常,你只会想使用父类来保存你不想要键入每个子模型的信息.这种类几乎不会单独使用,那么抽象基类(Abstract base class)就是你要的.
2. 如果你继承了一个已存在的模型(也许是另外一个应用),并希望每个模型都有自己的数据库表,就使用多表继承(Multi-table inheritance).
3. 最后,如果你只想修改一个模型的Python-level behavior,而不改变模型的字段,你可以使用代理模型(Proxy models).
当你想把一些公共信息保存到另外的模型时抽象基类很有用.写一个基类并在Meta类里添加abstract=True.这个模型不会在数据库里建任何表.当它用于作其他模型的基类时,它的字段会被添加到子类里.如果父类有字段名和子类相同,django会抛出异常.
一个例子:
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模型会有3个字段:name, age和home_group.CommonInfo模型不能挡着普通的django模型来使用,因为它是一个抽象类.它不会生成数据库表,没有manager,不能实例化,不能直接保存(saved directly).
你可以使用这种形式的模型继承应对大多是情况.它提供了一个方法在python层面来抽取公共信息,数据库层面一个子类一个表.
from django.db import Model
class CommonInfo(models.Model):
# ...
class Meta:
abstract = True
ordering= ['name']
class Student(CommonInfo):
# ...
class Meta(CommonInfo.Meta):
db_table = 'student_info'
django会对抽象基类的Meta类有一个调节(adjustment):在使用Meta属性前,设置abstract=True.这就是说抽象基类的子类不会自动变成抽象类.当然,你也可以是一个抽象基类继承自另一个抽象基类.你只要记得显式的设置abstract=True就行了.
有些属性出现在抽象基类的Meta类里就没有意义了.例如,其中有db_table就表示子类(没有定义自己的Meta)将会使用同样的数据库表,这肯定不是你想要的.
例如,common/models.py:
from django.db import models
class Base(models.Model):
m2m = models.ManyToManyField(OtherModel, related_name="%(app_label)s_%(class)s_related")
class Meta:
abstract = True
class ChildA(Base):
pass
class ChildB(Base):
pass
另一个app rare/models.py:
from common.models import Base
class ChildB(Base):
pass
common.ChildA.m2m字段的反向名称(reverse name)是common_childa_related,同时common.ChildB.m2m字段的反向名称时common_childb_related,最后rare.ChildB.m2m字段的反向名称时rare_childb_related.这取决于你怎么使用’%(class)s’和’%(app_label)s’来组成你的关联名称,但是如果你忘记使用,在使用系统检查(或运行migrate)时django会抛出异常.
如果你没有在抽象基类里为字段指定related_name属性,默认的反向名称将是子类的名称跟着’_set’,就像是你直接在子类里声明变量时正常生成的那样.例如,在上面的代码中,如果related_name属性省略了,m2m字段的反向名称在ChildA里是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=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)
# 如果p是一个Restaurant对象,就会得到子类:
>>> p.restaurant
但是,如果上面例子中的p不是一个Restaurant(它是直接创建的Place对象或是其他类的父类),p.restaurant就会抛出Restaurant.DoesNotExist异常.
class ChildModel(ParentModel):
# ...
class Meta:
# 移除父类的ordering影响
ordering = []
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’)
使用多表继承时,每个子模型都有一个数据库表.通常这就是你想要的,因为子类需要一个地方来存储另外的父类没有的数据字段.但是,有时候,你只是想更改模型的python行为-也许是更改默认的manager,或添加一个新方法.
这就是代理模型要做的:为原始模型创建一个代理.你可以创建,删除和更新代理模型的实例并且所有的数据会像原始模型一样保存.不同之处在于你可以在代理模型中更改一些东西,比如默认的ordering或默认的manager,而不必改变原始模型.
代理模型声明方式和普通的模型一样.你通过设置Meta类中的proxy属性为True来告诉django这是一个代理模型.
例如,假设你想给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')
你也可以使用代理模型来定义不同的ordering.你可能不会将Person模型排序,但使用代理时可以更具last_name属性排序.这很容易:
class OrderedPerson(Person):
class Meta:
ordering = ['last_name']
proxy = True
现在普通的Person查询不会排序,OrderedPerson查询会根据last_name排序.
QuerySets still return the model that was required
django没办法使Person查询返回MyPerson对象.查询Person还是返回Person类型的对象.The whole point of proxy objects is that code relying on the original Person will use those and your own code can use the extensions you included (that no other code is relying on anyway). It is not a way to replace the Person (or any other) model everywhere with something of your own creation.
Base class restrictions
一个代理模型必须继承自一个非抽象类,不能继承自多个非抽象类.一个代理模型能继承多个抽象模型类,如果他们没有定义任何模型字段.
Proxy model managers
如果你没有在代理模型里指定manager,它就会继承父模型的manager.如果你定义了manager,它就会变成末日呢的,虽然父类里定义的manager仍然可用.
继续上面的例子,你可以查询Person模型时可以改变默认的manager:
from django.db import models
class NewManger(models.Manager):
# ...
pass
class MyPerson(Person):
objects = NewManager()
class Meta:
proxy = True
如果你想给代理模型添加新的manager,而不代替默认的,你可以c使用ustom manager文档里介绍的技巧:新建一个基类包含新的manager,然后继承它.
# 为新manager创建一个抽象类.
class ExtraManagers(models.Model):
secondary = NewManger()
class Meta:
abstract = True
class MyPerson(Person, ExtraMangers):
class Meta:
proxy = True
你可能很少需要这样用,但是当你需要时,这就很必要了.
就像python的子类一样,django模型有可能继承自多个父模型.Keep in mind that normal Python name resolution rules apply.特殊名称(例如Meta)出现的第一个基类将会被使用;例如,如果多个父类都有Meta类,只有第一个会被使用,其他的会忽略.
通常,没有必要使用多继承.主要使用”mix-in”类:adding a particular extra field or method to every class that inherits the mix-in.尽量使你的继承关系简单明了,这样你就不会被数据到底是哪来的搞的头大了.
django1.7更改
在django1.7之前,继承自多个模型,并有一个id主键字段不会抛出异常,但是会导致数据丢失.例如,看看这些模型(因为冲突的id字段所以不再验证):
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 "" , line 1, in
AssertionError
>>> # "Some piece of news."headline被重写了.
>>> Article.objects.get(pk=article.pk).headline
'Review of Little Red Riding Hood.'
为了正常使用多继承,你可以在基类中使用显式的AutoField:
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(Or use a common ancestor to hold the AutoField):
class Piece(models.Model):
pass
class Article(Piece):
...
class Book(Piece):
...
class BookReview(Book, Article):
...
在普通的python类继承中,子类可以重写父类的任何属性.在django里,重写字段实例属性是不允许的(至少目前不允许).如果基类有个字段叫author,你不能在子类中创建一个字段叫author.
重写父类的字段涉及到很复杂的问题,比如初始化新实例(在Model.init里指定实例化哪个字段)和序列化.这些特性不能用普通的python类继承来处理,所以django模型继承和python类继承有很大的不同.
这个限制只针对于字段实例属性.普通的python属性可以被重写,如果你愿意. It also only applies to the name of the attribute as Python sees it:如果你手动指定了数据库字段名,在多表继承中,你能在子类和父类中看到相同的字段名出现(在2个不同的数据库表中的字段).
如果你重写父模型中的模型字段,django会抛出FieldError异常.