Django:如何验证m2m关系?

2024-04-24 19:09:39 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我有一个Basket模型,我想验证不能向其添加超过5Items:

class Basket(models.Model):
    items = models.ManyToManyField('Item')

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

    def clean(self):
        super(Basket, self).clean()
        if self.items.count() > 5:
            raise ValidationError('This basket can\'t have so many items')

但是当试图保存Basket时,由于超过了最大递归深度,因此抛出了RuntimeError

错误如下:

^{pr2}$

它发生在if self.items.count() > 5:行中。在

显然,Django的复杂性不允许您在保存模型时验证m2m关系。那么我如何验证它们呢?在


Tags: 模型selfcleanmodelifmodelssavedef
2条回答

您可以在模型的clean方法中从不验证关系。这是因为在清洁时间,模型可能还不存在,就像你的篮子一样。不存在的东西,也不能有关系。在

您要么需要对@bhattravii指出的form data进行验证,要么调用form.save(commit=False)并实现一个名为^{}的方法,该方法实现了限制。在

要在模型级别实施限制,您需要监听^{}信号。请注意,向最终用户提供反馈要困难得多,但它确实可以通过不同的方式防止过多的反馈。在

我已经在Django开发者列表上讨论过这一点,事实上,我已经提出了一种方法,以某种形式在Django核心中加以考虑。该方法还没有完全测试,也没有最终确定,但目前的结果是非常令人鼓舞的,我正在我的网站上成功地使用它。在

原则上,它依赖于:

  1. 使用PostgreSQL作为您的数据库引擎(我们非常肯定它不会 在Lightdb或MySQL上工作,但希望任何人都能测试一下) 在此处输入代码

  2. 重写(基于类的)视图的post()方法,以便它:

    1. 打开原子事务
    2. 保存表单
    3. 保存所有表单集(如果有)
    4. 电话模型.清洁()或者其他类似的东西型号:完全清洁()
  3. 在你的模型中,在上面2.4中调用的方法中,你将看到你所有的多对多和一对多关系。您可以验证它们并抛出ValidationError来查看整个事务回滚,而不会对数据库产生影响。在

这对我来说非常有效:

def post(self, request, *args, **kwargs):
    # The self.object atttribute MUST exist and be None in a CreateView. 
    self.object = None
    self.form = self.get_form()     
    self.success_url = reverse_lazy('view', kwargs=self.kwargs)

    if connection.vendor == 'postgresql':
        if self.form.is_valid():
            try:
                with transaction.atomic():
                    self.object = self.form.save()
                    save_related_forms(self) # A separate routine that collects all the formsets in the request and saves them

                    if (hasattr(self.object, 'full_clean') and callable(self.object.full_clean)):
                        self.object.full_clean()
            except (IntegrityError, ValidationError) as e:
                if hasattr(e, 'error_dict') and isinstance(e.error_dict, dict):
                    for field, errors in e.error_dict.items():
                        for error in errors:
                            self.form.add_error(field, error)
                return self.form_invalid(self.form)                    

            return self.form_valid(self.form)
        else:
            return self.form_invalid(self.form)

    else:
        # The standard Djangop post() method
        if self.form.is_valid():
            self.object = self.form.save()
            save_related_forms(self)
            return self.form_valid(self.form)
        else:
            return self.form_invalid(self.form)

开发者名单上的对话如下:

https://groups.google.com/forum/#!topic/django-developers/pQ-8LmFhXFg

如果您想贡献您从实验中获得的任何经验(可能是其他数据库后端)。在

上述方法的一个重要警告是,它将保存委托给post()方法,在默认视图中,post()方法是在form_valid()方法中完成的,因此还需要重写form_valid(),否则像上面这样的post()会看到您保存表单两次。这只是在UpdateView上浪费时间,而在CreateView上则是灾难性的。在

相关问题 更多 >