Django的ModelForm unique_together验证

stt*_*ter 57 django validation modelform

我有一个看起来像这样的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")
Run Code Online (Sandbox Code Playgroud)

我使用表单添加如下所示的模型:

class SolutionForm(forms.ModelForm):
    class Meta:
        model = Solution
        exclude = ['problem']
Run Code Online (Sandbox Code Playgroud)

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

谢谢.

小智 36

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


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)
Run Code Online (Sandbox Code Playgroud)

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


Dan*_*man 25

正如Felix所说,ModelForms应该检查unique_together其验证中的约束.

但是,在您的情况下,您实际上是从表单中排除该约束的一个元素.我想这是你的问题 - 如果表格的一半甚至不在表格上,表格将如何检查约束?

  • 确实是那个问题.所以我想我不能在没有包含问题字段的情况下在表单上得到错误,并且我将不得不手动检查这种情况. (2认同)

stt*_*ter 19

我设法通过在表单中​​添加一个干净的方法来修改它而不修改视图:

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
Run Code Online (Sandbox Code Playgroud)

我现在在视图中唯一需要做的就是在执行之前向表单添加一个问题属性is_valid.

  • 不要使用bare except子句.即使异常是由于数据库服务器被流星击中,这也会通过.相反,使用"除Solution.DoesNotExist:". (11认同)

小智 8

来自@sttwister的解决方案是正确的,但可以简化.

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
Run Code Online (Sandbox Code Playgroud)

作为奖励,您不会在重复的情况下检索该对象,但只检查它是否存在于数据库中,从而节省了一些性能.