Django的ModelForm unique_togethervalidation

我有一个Django模型,看起来像这样。

class Solution(models.Model): ''' Represents a solution to a specific problem. ''' name = models.CharField(max_length=50) problem = models.ForeignKey(Problem) description = models.TextField(blank=True) date = models.DateTimeField(auto_now_add=True) class Meta: unique_together = ("name", "problem") 

我使用表单来添加看起来像这样的模型:

 class SolutionForm(forms.ModelForm): class Meta: model = Solution exclude = ['problem'] 

我的问题是, SolutionForm不validationSolutionunique_together约束,因此,它在尝试保存表单时返回一个IntegrityError 。 我知道我可以使用validate_unique手动检查这一点,但我想知道是否有任何方式来捕获表单validation,并自动返回一个表单错误。

谢谢。

我通过覆盖ModelForm的validate_unique()方法解决了这个问题:

 def validate_unique(self): exclude = self._get_validation_exclusions() exclude.remove('problem') # allow checking against the missing attribute try: self.instance.validate_unique(exclude=exclude) except ValidationError, e: self._update_errors(e.message_dict) 

现在我只是总是确保表单上没有提供的属性仍然可用,例如初始化器上的instance=Solution(problem=some_problem)

正如Felix所说,ModelForms应该在validation中检查unique_together约束。

但是,在你的情况下,你实际上从你的表单中排除了这个约束的一个元素。 我想这是你的问题 – 表单如何检查约束,如果它的一半甚至不在表单上?

我设法解决这个问题,而不是通过添加一个干净的方法来修改视图:

 class SolutionForm(forms.ModelForm): class Meta: model = Solution exclude = ['problem'] def clean(self): cleaned_data = self.cleaned_data try: Solution.objects.get(name=cleaned_data['name'], problem=self.problem) except Solution.DoesNotExist: pass else: raise ValidationError('Solution with this Name already exists for this problem') # Always return cleaned_data return cleaned_data 

我现在唯一需要做的是在执行is_valid之前向表单添加问题属性。

@twtwister的解决scheme是正确的,但可以简化。

 class SolutionForm(forms.ModelForm): class Meta: model = Solution exclude = ['problem'] def clean(self): cleaned_data = self.cleaned_data if Solution.objects.filter(name=cleaned_data['name'], problem=self.problem).exists(): raise ValidationError( 'Solution with this Name already exists for this problem') # Always return cleaned_data return cleaned_data 

作为奖励,您不要在重复的情况下检索对象,而只是检查数据库中是否存在一些performance。

在Jarmo的答案的帮助下,下面似乎很好地为我工作(在Django 1.3中),但是我可能已经打破了一些angular落情况(围绕_get_validation_exclusions有很多票):

 class SolutionForm(forms.ModelForm): class Meta: model = Solution exclude = ['problem'] def _get_validation_exclusions(self): exclude = super(SolutionForm, self)._get_validation_exclusions() exclude.remove('problem') return exclude 

我不确定,但是这对我来说似乎是一个Django的bug …但我不得不看看以前报告的问题。


编辑:我说得太快了。 也许我上面写的会在某些情况下有效,但不是在我的情况下。 我最终直接使用Jarmo的答案。

你将需要做这样的事情:

 def your_view(request): if request.method == 'GET': form = SolutionForm() elif request.method == 'POST': problem = ... # logic to find the problem instance solution = Solution(problem=problem) # or solution.problem = problem form = SolutionForm(request.POST, instance=solution) # the form will validate because the problem has been provided on solution instance if form.is_valid(): solution = form.save() # redirect or return other response # show the form 

如果您希望错误消息是与name字段相关联(并显示在其旁边):

 def clean(self): cleaned_data = super().clean() name_field = 'name' name = cleaned_data.get(name_field) if name: if Solution.objects.filter(name=name, problem=self.problem).exists(): cleaned_data.pop(name_field) # is also done by add_error self.add_error(name_field, _('There is already a solution with this name.')) return cleaned_data