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
不validationSolution
的unique_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