在GenericForeignKey

时间:2018-08-07 18:03:10

标签: django django-1.11

我有一个使用GenericForeignKey对象的简单模型。我想将允许的content_objects限制为一组特定的静态模型。可以说,我只希望它分别接受ModelAModelB的{​​{1}}和app_a

我碰到了this question,它从根本上描述了我要达到的目标。我实施了建议的解决方案,最后得到的模型看起来像这样:

app_b

当使用/ admin /面板添加对象时,这实际上似乎正常工作,class TaggedItem(models.Model): tag = models.SlugField() content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE) object_id = models.PositiveIntegerField() limit = models.Q(app_label='app_a', model='modela') \ | models.Q(app_label='app_b', model='modelb') content_type = models.ForeignKey(ContentType, limit_choices_to=limit) content_object = GenericForeignKey('content_type', 'object_id') 从我的可用选项中拉出。但是,当我编写单元测试或使用外壳程序时,似乎并没有强制执行此操作。

例如,我期望:

content_type

引发异常。但是,事实并非如此。有没有一种强制使用TaggedItem.objects.create(content_object=(ModelZ())) 作为content_objects中给出的模型实例的django-istic方法?

1 个答案:

答案 0 :(得分:1)

在Django中,默认情况下choices=...在模型层中不是强制执行的。因此,如果.save().create(..)的模型对象,则列中的值可能不是相应choices的成员。然而,ModelForm对对象执行full_clean(..),从而强制执行此操作。

但是Django模型具有,但是可以强制执行此方法:如果调用.full_clean(..)函数,则如果值不是有效选择,它将引发错误。因此,我们可以使用以下方法修补单个模型:

class TaggedItem(models.Model):
    tag = models.SlugField()
    content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
    object_id = models.PositiveIntegerField()
    limit = models.Q(app_label='app_a', model='modela') \
            | models.Q(app_label='app_b', model='modelb')
    content_type = models.ForeignKey(ContentType, limit_choices_to=limit)
    content_object = GenericForeignKey('content_type', 'object_id')

    def save(self, *args, **kwargs):
        self.full_clean()
        super().save(*args, **kwargs)

因此,每次您.save(..)使用某个功能时,这都会检查选择。 This question有几个答案,它们提供了针对特定模型或所有模型的替代方法。

但是请记住,Django ORM仍然允许绕过它。例如,TaggedItem.objects.update(content_type=1425)仍然可以成功(因为它直接映射到SQL查询中),所以没有办法以通用的方式(在所有数据库系统中)强制执行此操作。 Django ORM(部分出于性能原因,部分出于向后兼容性)允许进行查询,这些查询可以使数据库进入“无效状态”(本身对数据库无效,但对Django模型层无效)。 / p>