Django 在GenericForeignKey中强制执行模型限制

Django 在GenericForeignKey中强制执行模型限制,django,django-1.11,Django,Django 1.11,我有一个使用对象的简单模型。我想将允许的content\u对象限制为一组特定的静态模型。比方说,我只希望它分别接受来自app_a和app_b的ModelA和ModelB 我偶然发现,这基本上描述了我正在努力实现的目标。我实施了提议的解决方案,最终得到了一个类似以下内容的模型: class TaggedItem(models.Model): tag = models.SlugField() content_type = models.ForeignKey(ContentType,

我有一个使用对象的简单模型。我想将允许的
content\u对象
限制为一组特定的静态模型。比方说,我只希望它分别接受来自
app_a
app_b
ModelA
ModelB

我偶然发现,这基本上描述了我正在努力实现的目标。我实施了提议的解决方案,最终得到了一个类似以下内容的模型:

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')
当使用/admin/panel添加对象时,
content\u类型
从我的可用选项中提取时,这实际上似乎工作正常。然而,当我编写单元测试或使用shell时,它似乎没有强制执行这一点

例如,我希望:

TaggedItem.objects.create(content_object=(ModelZ()))

提出一个例外。然而,事实并非如此。是否有任何django-istic方法强制将
内容对象
作为
限制选项
中给定模型的实例

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

然而,Django模型有一种方法来实现这一点:如果调用
.full\u clean(..)
函数,如果值不是有效的选择,它将引发错误。因此,我们可以使用以下内容修补单个模型:

class TaggedItem(models.Model):
    tag = models.SlugField()
    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)
class标记数据项(models.Model):
tag=models.SlugField()
object_id=models.PositiveIntegerField()
limit=models.Q(app\u label='app\u a',model='modela')\
|models.Q(app_label='app_b',model='modelb')
content\u type=models.ForeignKey(ContentType,limit\u选项\u to=limit)
content\u object=GenericForeignKey('content\u type','object\u id')
def保存(自身、*args、**kwargs):
self.full_clean()
super().save(*args,**kwargs)
因此,每次保存(…)
函数时,都会检查选项。在特定模型或所有模型上,有几个答案提供了执行此操作的替代方法


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

模型从不验证
选择,只有相关的
模型表单才能验证。当然,您可以在
.save()
中进行一些额外的验证,但是有一些方法可以“绕过”
.save()
。非常感谢您提供了这个非常好的答案—这正是我需要知道的。我正在重新思考GenericForiegnKey的使用以及处理我的用例所需的GenericRelations。我想这可能是故意破坏代码。@rob:a
GenericForeignKey
是一个相当“有争议”的话题。虽然可能有一些用例,但我认为只有有限数量的用例最好使用
GenericForeignKey
。当然,问题仍然是什么用例:)。