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 ,更不能直接被实例化和保存。

扫描二维码关注公众号,回复: 9464293 查看本文章

对很多应用来说,这种继承方式正是你想要的。它提供一种在 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继承

发布了115 篇原创文章 · 获赞 4 · 访问量 4607

猜你喜欢

转载自blog.csdn.net/weixin_43999327/article/details/104226340
今日推荐