我有一个使用GenericForeignKey对象的简单模型。我想将允许的content_objects
限制为一组特定的静态模型。可以说,我只希望它分别接受ModelA
和ModelB
的{{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方法?
答案 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>