django - Django 中的 M2M 关系验证

标签 django validation django-models

我有这两个模型:

class Test(models.Model):
    problems = models.ManyToManyField('Problem')
    ...

class Problem(models.Model):
    type = models.CharField(max_length=3, choices=SOME_CHOICES)
    ...

现在,在将问题添加到测试时,我需要限制测试中特定类型问题的数量。例如。一个测试只能包含3个A类型的问题,依此类推。

验证这一点的唯一方法似乎是在 Test.problems.through 表上使用 m2m_changed 信号。但是,要进行验证,我需要访问当前添加的 Problem 以及现有的 Problems - 这似乎在某种程度上是不可能的。

做这样的事情的正确方法是什么? M2M 验证似乎是文档中未涉及的主题。我错过了什么?

最佳答案

您说得对,您必须注册 m2m_changed 信号函数,如下所示:

def my_callback(sender, instance, action, reverse, model, pk_set, **kwargs)

如果您阅读文档,您会发现 sender 是触发更改的对象模型,而 model 是将要更改的对象模型。 pk_set 将为您提供 pkey,这些 pkey 将成为您模型的新引用。因此,在您的测试模型中,您必须执行以下操作:

@receiver(m2m_changed)
def my_callback(sender, instance, action, reverse, model, pk_set, **kwargs):
    if action == "pre_add":
        problem_types = [x.type for x in model.objects.filter(id__in=pk_set)]
        if problem_types.count("A") > some_number:
            raise SomeException

请注意,如果您从 Django 管理站点输入字段,则不会捕获该级别的异常。为了能够为 django 管理数据输入提供用户友好的错误,您必须将自己的表单注册为管理表单。对于您的情况,您需要执行以下操作:

class ProblemTypeValidatorForm(ModelForm):
    def clean(self):
        super(ProblemTypeValidatorForm, self).clean()
        problem_types = [x.type for x in self.cleaned_data.get("problems") if x]
        if problem_types.count("A") > some_number:
            raise ValidationError("Cannot have more than {0} problems of type {1}"
                                  .format(len(problem_types), "A")

然后在你的admin.py

@admin.register(Test)
class TestAdmin(admin.ModelAdmin):
    form = ProblemTypeValidatorForm

现在请记住,这是两个不同级别的实现。没有人可以保护您免受有人手动执行此操作的影响:

one_test_object.problems.add(*Problem.objects.all())
one_test_object.save()

个人意见:

因此,请记住上述内容,我建议您采用 ModelForm 和 ModelAdmin 方法,如果您为 CRUD 操作提供 API,也请在那里进行验证。没有什么可以保护你免受有人通过 django shell 在你的数据库中输入内容的影响。如果您想要这样的解决方案类型,您应该直接进入数据库并编写某种神奇的触发脚本。但请记住,您的数据库实际上是数据。您的后端是具有业务逻辑的后端。因此,您不应该真正尝试将业务规则强加到数据库级别。通过在创建/更新发生的位置验证数据,将规则保留在后端。

关于django - Django 中的 M2M 关系验证,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/29615800/

相关文章:

Mysql 无法创建/写入文件错误#13

javascript - 如何使用 jQuery 验证美国邮政编码并在 Bootstrap 模板中的文本字段上方以红色显示错误消息?

python - 无法从在 Docker 容器中运行的 Django 应用程序连接到远程 PostgreSQL 实例

django - 尝试使用 Redis 和随机 token 实现无密码身份验证

reactjs - webpack-cli不能同时运行多个命令

python - django-simple-history 无法导入管理命令中模型导入的名称错误

django-models - 在 Django 中创建用户通知系统

ruby-on-rails - 如何验证 has_many 关系没有改变

jquery - 提交前监听语义 UI 表单验证错误

Django,如何用 django-mptt 做 CRUD?