我们说我有一个Basket
模型,我想验证只能添加5
Item
个:
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
,因为超出了最大递归深度。
错误如下:
ValueError: "<Basket: Basket>" needs to have a value for field "basket" before this many-to-many relationship can be used.
它发生在if self.items.count() > 5:
行。
显然,Django的复杂功能只是让您在保存模型时无法验证m2m关系。我怎样才能验证它们呢?
答案 0 :(得分:1)
您可以从不验证模型的clean方法中的关系。这是因为在清洁时,模型可能尚不存在,就像您的购物篮一样。不存在的东西,也可能没有关系。
您需要根据@bhattravii指出的form data进行验证,或者调用form.save(commit=False)
并实现一个名为save_m2m
的方法,该方法实现了限制。
要在模型级别强制执行限制,您需要收听m2m_changed
信号。请注意,向最终用户提供反馈要困难得多,但它确实可以防止通过不同方式过度填充篮子。
答案 1 :(得分:1)
我一直在Django Developers列表上讨论此问题,实际上已经提出了一种这样做的方法,以一种或另一种形式在Django核心中考虑。该方法尚未经过全面测试或最终确定,但目前的结果令人鼓舞,我在我的站点上成功使用了该方法。
原则上,它依赖于:
使用PostgreSQL作为数据库引擎(我们相当确定它不会 在Lightdb或MySQL上工作,但热衷于任何人进行测试) 在此处输入代码
覆盖您的(基于类)视图的post()方法,使其:
这对我来说很棒:
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()方法,该方法在默认视图中由form_valid()方法完成,因此您还需要覆盖form_valid(),否则需要覆盖post() ),就像上面的一样,您会看到两次保存表格。在UpdateView上只是浪费时间,而在CreateView上却是灾难性的。