在Django中,与抽象模型相关的ForeignKey字段

36

我有这个模型:

class BaseModel(models.Model):
    ....

    class Meta:
        abstract = True


class ModelA(BaseModel):
    ....

class ModelB(BaseModel):
    ....


class MyExtModel(models.Model)
    myfield = models.ForeignKey(BaseModel)

但这是不正确的,因为我有一个名为BaseModel的抽象模型。实际上,当我尝试使用makemigrations命令时,我遇到了错误。

错误信息如下:

ERRORS:
myapp.MyExtModel.myfield: (fields.E300) Field defines a relation with model 'BaseModel', which is either not installed, or is abstract.

有没有一种方法可以使用抽象基本模型?

我也尝试使用:

myfield = models.ForeignKey(BaseModel, related_name="%(app_label)s_%(class)s_related")
3个回答

42

在Django中,无法为抽象模型安装外键。但是,您可以将外键安装到非抽象基类中。唯一的限制是反向外键关系将返回基类实例。您可以通过使用django-polymorphic来规避此限制。

Django Polymorphic允许您查询基类对象,但检索子类实例。

>>> Project.objects.create(topic="Department Party")
>>> ArtProject.objects.create(topic="Painting with Tim", artist="T. Turner")
>>> ResearchProject.objects.create(topic="Swallow Aerodynamics", supervisor="Dr. Winter")

>>> Project.objects.all()
[ <Project:         id 1, topic "Department Party">,
  <ArtProject:      id 2, topic "Painting with Tim", artist "T. Turner">,
  <ResearchProject: id 3, topic "Swallow Aerodynamics", supervisor "Dr. Winter"> ]

为了使用Django Polymorphic,您只需要将Polymorphic Model作为基类声明您的模型:

from django.db import models
from polymorphic.models import PolymorphicModel

class ModelA(PolymorphicModel):
    field1 = models.CharField(max_length=10)

class ModelB(ModelA):
    field2 = models.CharField(max_length=10)

class ModelC(ModelB):
    field3 = models.CharField(max_length=10)

外键也会返回子类的实例,我想这就是你所需要的:

# The model holding the relation may be any kind of model, polymorphic or not
class RelatingModel(models.Model):
    many2many = models.ManyToManyField('ModelA')  # ManyToMany relation to a polymorphic model

>>> o=RelatingModel.objects.create()
>>> o.many2many.add(ModelA.objects.get(id=1))
>>> o.many2many.add(ModelB.objects.get(id=2))
>>> o.many2many.add(ModelC.objects.get(id=3))

>>> o.many2many.all()
[ <ModelA: id 1, field1 (CharField)>,
  <ModelB: id 2, field1 (CharField), field2 (CharField)>,
  <ModelC: id 3, field1 (CharField), field2 (CharField), field3 (CharField)> ]

请考虑这些查询将会略微降低性能


2
我之前不知道PolymorphicModel...也许这可以帮助我。我不理解一个方面:PolymorphicModel是基于GenericRelation吗?什么时候需要使用GenericRelation(content-type)而不是PolymorphicModel?也许这个问题与我的原始问题无关... - Safari
1
通用关系与多态模型无关。通用关系对于您的应用程序中具有基本不同模型的外键的通用模型非常有用。我在我的应用程序中有一个通用的图像模型,事件和团队模型都可以拥有图像。这是一种通用关系。我还有一个从Team继承的InternationalTeam模型,然后所有的团队都会有图像,而无需在模型中明确指定。 - Sebastian Wozny

20
当我面临需要在不同模型之间创建ForeignKeys的情况时,我选择使用GenericForeignKey,您可以在这里查看官方文档:Django ContentTypes: Generic Relations 文档已经很好地解释了如何使用它:
from django.db import models
from django.contrib.contenttypes.fields import GenericForeignKey
from django.contrib.contenttypes.models import ContentType

class TaggedItem(models.Model):
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

    def __str__(self):              # __unicode__ on Python 2
        return self.tag
  • 字段content_type存储通用外键指向的模型

  • 字段object_id存储外键的ID

  • 字段content_object基于其他2个字段帮助您直接访问相关对象

这不是最好的解决方案,但在某些项目中可以节省时间。

使用示例:

from django.contrib.auth.models import User
guido = User.objects.get(username='Guido')
t = TaggedItem(content_object=guido, tag='bdfl')
t.save()
t.content_object
<User: Guido>

4
除了那个我不太熟悉的GenericForeignKey的好答案外,有时(仅当可能时),使用一对一关系简化您的“基本”模型会很有价值。
这样做之后,外键管理变得更加容易。如果我记得正确,抽象类上的外键是不可能的。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接