Django模型继承问题. 如何解决?

1 投票
3 回答
614 浏览
提问于 2025-04-16 05:04

我有一个现成的应用,里面有一个模型

class Contact(models.Model):
     lastname = models.CharField(max_length=200)
     firstname = models.CharField(max_length=200)
     ...

class Journalist(Contact):
     pass

我在数据库里有一个Contact(联系人),我想把它变成一个Journalist(记者)。

如果用原始的SQL语句,这看起来很简单,比如说insert into app_journalist values (25624);。在这个例子里,25624是现有联系人的ID。这样做似乎没问题,django应用也没有报错。

不过,我想用django的ORM来实现同样的功能。我尝试了几种方法,比如强制指定记者的ID(Journalist(id=25624)),但这样会创建一个新的联系人,而不是链接到已有的那个。

用Django的ORM能做到这一点吗?怎么做呢?

提前感谢你的帮助

3 个回答

0

在模型层面上使用继承通常不是个好主意。大多数ORM(对象关系映射)工具都允许你这样做,甚至为此感到自豪。但在模型层面上,著名的“更喜欢组合而不是继承”这句话比以往任何时候都更真实。

在你的例子中,不如说:“一个记者是一个人”,可以改成:“一个人有一个工作,而这个工作是记者”。这可以通过一个“人”类和一个“工作”类来表示。工作之一可以是“记者”。

使用组合的方法可以让你方便地改变一个人的工作,甚至让一个人有多个工作。

当然,这并没有直接回答你的问题,但其他的回答已经相当不错了!

2

Django的内容类型框架非常实用。你可以用它来表示不同的联系方式类型:

from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.contenttypes import generic

class Contact(models.Model):
     lastname = models.CharField(max_length=200)
     firstname = models.CharField(max_length=200)
     content_object = generic.GenericForeignKey('content_type', 'object_id')
3

解决这个问题的一种方法是(不改变你的模型结构)将Journalist实例的contact_ptr属性设置为相应的Contact实例。例如:

contact = Contact.objects.get(pk = 25624)
journalist = Journalist(contact_ptr = contact)
journalist.save()

如果你先看看app_journalist这个表,会更容易理解。这个表只有一列,叫contact_ptr_id。所以当你执行insert into app_journalist values (25624)时,你实际上是在SQL层面上设置contact_ptr_id = 25624。相应地,你应该在ORM层面上设置contact_ptr = <Contact的实例>

更新

还有其他方法可以解决这个问题,但它们需要对你现有的模型进行修改。正如@bugspy.net 指出的,你可以使用通用关系。或者,你可以声明一个额外的type字段,用来指定这个联系人是记者、同事等。

更新 2

还可以看看这个 演示代码(以及 完整代码),它让你使用多态继承(SQLAlchemy已经支持这个功能)。

更新 3

正如@luc自己指出的(见下面的评论)

journalist = Journalist(contact_ptr = contact)

单靠这个是不够的。这会把contactfirstnamelastname覆盖成""。为了避免这种情况,你必须明确地将每个字段分配给Journalist

撰写回答