Django - 模型关系:模型继承
Django中模型支持继承。
Django默认继承是将通用字段放在父类中,特定字段放在自己的表中,中间使用外键连接。
- 关系型数据库关系越复杂,查询效率越低。
- 父类表中也会存在过多的数据
使用元信息来解决这个问题:
- 使模型抽象化
- 抽象的模型就不会在数据库中产生映射了。
- 子模型映射出来的表直接包含父模型的字段。
1 概要
Django 中的 model 继承和 Python 中的类继承非常相似,只不过你要选择具体的实现方式:让父 model 拥有独立的数据库;还是让父 model 只包含基本的公共信息,而这些信息只能由子 model 呈现。
Django中有三种继承关系:
1.通常,你只是想用父 model 来保存那些你不想在子 model 中重复录入的信息。父类是不使用的也就是不生成单独的数据表,这种情况下使用抽象基类继承 Abstract base classes。
2.如果你想从现有的Model继承并让每个Model都有自己的数据表,那么使用多重表继承Multi-table inheritance。
3.最后,如果你只想在 model 中修改 Python-level 级的行为,而不涉及字段改变。 代理 model (Proxy models) 适用于这种场合。
2 Abstract base classes
如果你想把某些公共信息添加到很多 model 中,抽象基类就显得非常有用。你编写完基类之后,在Meta
内嵌类中设置 abstract=True
,该类就不能创建任何数据表。然而如果将它做为其他 model 的基类,那么该类的字段就会被添加到子类中。抽象基类和子类如果含有同名字段,就会导致错误(Django 将抛出异常)。
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)
SQL结果:
CREATE TABLE "myapp_student" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(100) NOT NULL,
"age" integer unsigned NOT NULL,
"home_group" varchar(5) NOT NULL
)
只为Student model
生成了数据表,而CommonInfo
不能做为普通的 Django model 使用,因为它是一个抽象基类。他即不生成数据表,也没有 manager ,更不能直接被实例化和保存。
对很多应用来说,这种继承方式正是你想要的。它提供一种在 Python 语言层级上提取公共信息的方式,但在数据库层级上,每个子类仍然只创建一个数据表,在JPA中称作TABLE_PER_CLASS。这种方式下,每张表都包含具体类和继承树上所有父类的字段。因为多个表中有重复字段,从整个继承树上来说,字段是冗余的。
2.1 Meta继承
创建抽象基类的时候,Django 会将你在基类中所声明的有效的 Meta 内嵌类做为一个属性。如果子类没有声明它自己的 Meta 内嵌类,它就会继承父类的 Meta 。子类的 Meta 也可以直接继承父类的 Meta 内嵌类,对其进行扩展。
例如:
class CommonInfo(models.Model):
name = models.CharField(max_length=100)
age = models.PositiveIntegerField()
class Meta:
abstract = True
ordering = ['name']
class Student(CommonInfo):
home_group = models.CharField(max_length=5)
class Meta(CommonInfo.Meta):
db_table = 'student_info'
sqlall结果:
CREATE TABLE "student_info" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(100) NOT NULL,
"age" integer unsigned NOT NULL,
"home_group" varchar(5) NOT NULL
)
按照我们指定的名称student_info
生成了table。
继承时,Django 会对基类的 Meta 内嵌类做一个调整:在安装 Meta 属性之前,Django 会设置 abstract=False
。 这意味着抽象基类的子类不会自动变成抽象类。当然,你可以让一个抽象类继承另一个抽象基类,不过每次都要显式地设置 abstract=True
。
对于抽象基类而言,有些属性放在 Meta 内嵌类里面是没有意义的。例如,包含 db_table 将意味着所有的子类(是指那些没有指定自己的 Meta 内嵌类的子类)都使用同一张数据表,一般来说,这并不是我们想要的。
3 多表继承(Multi-table inheritance)
这是 Django 支持的第二种继承方式。使用这种继承方式时,同一层级下的每个子 model 都是一个真正意义上完整的 model 。每个子 model 都有专属的数据表,都可以查询和创建数据表。继承关系在子 model 和它的每个父类之间都添加一个链接 (通过一个自动创建的 OneToOneField 来实现)。
例如:
class Place(models.Model):
name = models.CharField(max_length=50)
address = models.CharField(max_length=80)
class Restaurant(Place):
serves_hot_dogs = models.BooleanField()
serves_pizza = models.BooleanField()
sqlall:
BEGIN;
CREATE TABLE "myapp_place" (
"id" integer NOT NULL PRIMARY KEY,
"name" varchar(50) NOT NULL,
"address" varchar(80) NOT NULL
)
;
CREATE TABLE "myapp_restaurant" (
"place_ptr_id" integer NOT NULL PRIMARY KEY REFERENCES "myapp_place" ("id"),
"serves_hot_dogs" bool NOT NULL,
"serves_pizza" bool NOT NULL
)
;
COMMIT;
父类和子类都生成了单独的数据表,Restaurant中存储了Place的id,也就是通过OneToOneField链接在一起。继承关系通过表的JOIN操作来表示。在JPA中称作JOINED。这种方式下,每个表只包含类中定义的字段,不存在字段冗余,但是要同时操作子类和所有父类所对应的表。
Place 里面的所有字段在 Restaurant 中也是有效的,只不过数据保存在另外一张数据表当中。所以下面两个语句都是可以运行的:
>>> Place.objects.filter(name="Bob's Cafe")
>>> Restaurant.objects.filter(name="Bob's Cafe")
如果你有一个 Place,那么它同时也是一个 Restaurant, 那么你可以使用子 model 的小写形式从 Place 对象中获得与其对应的 Restaurant 对象:
>>> p = Place.objects.filter(name="Bob's Cafe")
# If Bob's Cafe is a Restaurant object, this will give the child class:
>>> p.restaurant
<Restaurant: ...>
但是,如果上例中的 p 并不是 Restaurant (比如它仅仅只是 Place 对象,或者它是其他类的父类),那么在引用 p.restaurant 就会抛开Restaurant.DoesNotExist 异常:
>>> from myapp.models import Place,Restaurant
>>> p=Place.objects.create(name='Place',address='Place')
>>> p.restaurant
DoesNotExist: Place has no restaurant.
也就是说,创建Place实例的同时不会创建Restaurant,但是创建Restaurant实例的同时会创建Place实例:
>>>Restaurant.objects.create(name='M',address='M',serves_hot_dogs=True,serves_pizza=True)
<Restaurant: Restaurant object>
>>> Place.objects.get(name='M')
<Place: Place object>
4 代理model (Proxy models)
详情请参看:Django中的model继承
还没有评论,来说两句吧...