75

私はこのような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検証しないため、フォームを保存しようとするとを返すことです。これを手動でチェックするために使用できることは知っていますが、フォームの検証でこれをキャッチし、フォームエラーを自動的に返す方法があるかどうか疑問に思いました。Solutionunique_togetherIntegrityErrorvalidate_unique

ありがとう。

4

9 に答える 9

39

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)

今では、フォームで提供されていない属性がまだ利用可能であることを常に確認しています。たとえばinstance=Solution(problem=some_problem)、イニシャライザです。

于 2010-09-21T06:40:03.057 に答える
29

フォームにクリーンなメソッドを追加することで、ビューを変更せずにこれを修正できました。

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です。

于 2010-01-28T16:03:00.150 に答える
28

Felix が言うように、ModelForms はunique_together検証で制約をチェックすることになっています。

ただし、あなたの場合、実際にはフォームからその制約の 1 つの要素を除外しています。これがあなたの問題だと思います-フォームの半分がフォーム上にない場合、フォームはどのように制約をチェックしますか?

于 2010-01-26T20:19:46.667 に答える
9

@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

おまけとして、重複の場合にオブジェクトを取得せず、データベースに存在するかどうかのみを確認して、パフォーマンスを少し節約します。

于 2015-05-05T10:03:17.667 に答える
1

Jarmoの答えの助けを借りて、以下は私にとってうまくいくようです(Django 1.3の場合)が、いくつかのコーナーケースを壊した可能性があります(周りにたくさんのチケットがあります_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 のバグのように思えます... しかし、以前に報告された問題を確認する必要があります。


編集: 話すのが早すぎました。たぶん、私が上に書いたことはいくつかの状況ではうまくいくかもしれませんが、私の場合はそうではありません。Jarmoの回答を直接使用することになりました。

于 2011-08-05T22:35:30.557 に答える
0

次のようなことをする必要があります。

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
于 2010-01-27T09:54:09.157 に答える
0

エラー メッセージをフィールドに関連付ける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
于 2016-01-15T09:45:41.523 に答える