Django 中有两种模型子类化——抽象基类;和多表继承。
抽象基类从不单独使用,也没有数据库表或任何形式的标识。它们只是一种缩短代码的方法,通过在代码中而不是在数据库中对公共字段集进行分组。
例如:
class Address(models.Model):
street = ...
city = ...
class Meta:
abstract = True
class Employee(Address):
name = ...
class Employer(Address):
employees = ...
company_name = ...
这是一个人为的例子,但正如您所见, anEmployee
不是 an Address
, an 也不是Employer
。它们都包含与地址相关的字段。此示例中只有两个表;Employee
, 和Employer
- 并且它们都包含地址的所有字段。无法在数据库级别将雇主地址与员工地址进行比较 - 地址没有自己的密钥。
现在,通过多表继承(从 Address 中删除 abstract=True),Address确实有一个自己的表。这将产生 3 个不同的表;Address
, Employer
, 和Employee
. Employer 和 Employee 都将有一个唯一的外键 (OneToOneField) 返回到 Address。
您现在可以引用地址,而不必担心它是什么类型的地址。
for address in Address.objects.all():
try:
print address.employer
except Employer.DoesNotExist: # must have been an employee
print address.employee
每个地址都有自己的主键,这意味着它可以单独保存在第四个表中:
class FakeAddresses(models.Model):
address = models.ForeignKey(Address)
note = ...
多表继承是您所追求的,如果您需要使用类型的对象Post
而不担心它是什么类型的 Post。如果从子类访问任何 Post 字段,将会产生连接开销;但开销将是最小的。这是一个唯一的索引连接,应该非常快。
只要确保,如果您需要访问您在查询集上Post
使用select_related
的 , 。
Events.objects.select_related(depth=1)
这将避免额外的查询来获取父数据,但会导致连接发生。因此,如果您需要 Post,请仅使用 select related。
两个最后的笔记;如果帖子既可以是公告也可以是事件,那么您需要做传统的事情,并通过 ForeignKey 链接到 Post。在这种情况下,没有子类化将起作用。
最后一件事是,如果连接对父子之间的性能至关重要,则应该使用抽象继承;并使用通用关系来引用表中的抽象帖子,这对性能的要求要低得多。
Generic Relations 本质上是这样存储数据的:
class GenericRelation(models.Model):
model = ...
model_key = ...
DeletedPosts(models.Model):
post = models.ForeignKey(GenericRelation)
在 SQL 中加入要复杂得多(django 可以帮助你),但它的性能也比简单的 OneToOne 连接要低。如果 OneToOne 连接严重损害了您的应用程序的性能,您应该只需要沿着这条路线走,这可能不太可能。