8

編集:この投稿はDjangoのModelForm unique_together検証の複製ですが、ModelFormから「exclude」を削除するというここでの受け入れられた答えは、他の質問で受け入れられた答えよりもはるかにクリーンなソリューションです。

これは、この質問のフォローアップです。

clean_title()関数でunique_together制約を明示的にチェックしないと、djangoは例外をスローします。

/ journal / journal/4のIntegrityError

重複するキー値が一意性制約「journal_journal_owner_id_key」に違反しています

リクエスト方法:POST

リクエストURL: http:// localhost:8000 / journal / journal / 4

例外タイプ:IntegrityError

例外値:重複するキー値が一意性制約「journal_journal_owner_id_key」に違反しています

例外の場所:実行中の/Library/Python/2.6/site-packages/django/db/backends/util.py、19行目

ただし、Djangoは、キャッチする必要のある例外を除いて、ValidationErrorを発生させることで、この制約を適切に適用するという印象を受けました。

以下は、回避策として使用する追加のclean_title()メソッドを使用したコードです。しかし、djangoが期待どおりに制約を適用しないように、私が間違っていることを知りたいです。

ありがとう。

モデルコード:

class Journal (models.Model):
    owner = models.ForeignKey(User, related_name='journals')
    title = models.CharField(null=False, max_length=256)
    published = models.BooleanField(default=False)

    class Meta:
        unique_together = ("owner", "title")

    def __unicode__(self):
        return self.title 

フォームコード:

class JournalForm (ModelForm):
    class Meta:
        model = models.Journal
        exclude = ('owner',)

    html_input = forms.CharField(label=u'Journal Content:', widget=TinyMCE(attrs={'cols':'85', 'rows':'40'}, ), )

    def clean_title(self):
        title = self.cleaned_data['title']
        if self.instance.id:
            if models.Journal.objects.filter(owner=self.instance.owner, title=title).exclude(id=self.instance.id).count() > 0:
               raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.')
        else:
            if models.Journal.objects.filter(owner=self.instance.owner, title=title).count() > 0:
               raise forms.ValidationError(u'You already have a Journal with that title. Please change your title so it is unique.')
        return title

コードを表示:

def journal (request, id=''):
    if not request.user.is_active:
        return _handle_login(request)
    owner = request.user
    try:
        if request.method == 'GET':
            if '' == id:
                form = forms.JournalForm(instance=owner)
                return shortcuts.render_to_response('journal/Journal.html', { 'form':form, })
            journal = models.Journal.objects.get(id=id)
            if request.user.id != journal.owner.id:
                return http.HttpResponseForbidden('<h1>Access denied</h1>')
            data = {
                'title' : journal.title,
                'html_input' : _journal_fields_to_HTML(journal.id),
                'published' : journal.published
            }
            form = forms.JournalForm(data, instance=journal)
            return shortcuts.render_to_response('journal/Journal.html', { 'form':form, })
        elif request.method == 'POST':
            if LOGIN_FORM_KEY in request.POST:
                return _handle_login(request)
            else:
                if '' == id:
                    journal = models.Journal()
                    journal.owner = owner
                else:
                    journal = models.Journal.objects.get(id=id)
                form = forms.JournalForm(data=request.POST, instance=journal)
                if form.is_valid():
                    journal.owner = owner
                    journal.title = form.cleaned_data['title']
                    journal.published = form.cleaned_data['published']
                    journal.save()
                    if _HTML_to_journal_fields(journal, form.cleaned_data['html_input']):
                        html_memo = "Save successful."
                    else:
                        html_memo = "Unable to save Journal."
                    return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'saved':html_memo})
                else:
                    return shortcuts.render_to_response('journal/Journal.html', { 'form':form })
        return http.HttpResponseNotAllowed(['GET', 'POST'])
    except models.Journal.DoesNotExist:
        return http.HttpResponseNotFound('<h1>Requested journal not found</h1>')

作業コードの更新: DanielRosemanに感謝します。

モデルコードは上記と同じです。

フォームコード-excludeステートメントとclean_title関数を削除します。

class JournalForm (ModelForm):
    class Meta:
        model = models.Journal

    html_input = forms.CharField(label=u'Journal Content:', widget=TinyMCE(attrs={'cols':'85', 'rows':'40'},),)

コードの表示-カスタムの一意性エラーメッセージを追加します。

def journal (request, id=''):
    if not request.user.is_active:
        return _handle_login(request)
    try:
        if '' != id:
            journal = models.Journal.objects.get(id=id)
            if request.user.id != journal.owner.id:
                return http.HttpResponseForbidden('<h1>Access denied</h1>')
        if request.method == 'GET':
            if '' == id:
                form = forms.JournalForm()
            else:
                form = forms.JournalForm(initial={'html_input':_journal_fields_to_HTML(journal.id)},instance=journal)
            return shortcuts.render_to_response('journal/Journal.html', { 'form':form, })
        elif request.method == 'POST':
            if LOGIN_FORM_KEY in request.POST:
                return _handle_login(request)
            data = request.POST.copy()
            data['owner'] = request.user.id
            if '' == id:
                form = forms.JournalForm(data)
            else:
                form = forms.JournalForm(data, instance=journal)
            if form.is_valid():
                journal = form.save()
                if _HTML_to_journal_fields(journal, form.cleaned_data['html_input']):
                    html_memo = "Save successful."
                else:
                    html_memo = "Unable to save Journal."
                return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'saved':html_memo})
            else:
                if form.unique_error_message:
                    err_message = u'You already have a Lab Journal with that title. Please change your title so it is unique.'
                else:
                    err_message = form.errors
                return shortcuts.render_to_response('journal/Journal.html', { 'form':form, 'error_message':err_message})
        return http.HttpResponseNotAllowed(['GET', 'POST'])
    except models.Journal.DoesNotExist:
        return http.HttpResponseNotFound('<h1>Requested journal not found</h1>')
4

2 に答える 2

9

問題は、一意のチェックに関係するフィールドの1つを明確に除外していることです。この状況では、Djangoはチェックを実行しません。_get_unique_checksの722行目のメソッドを参照してくださいdjango.db.models.base

フィールドを除外する代わりにowner、テンプレートからフィールドを除外し、インスタンス化時に渡すデータに値を明示的に設定することを検討します。

 data = request.POST.copy()
 data['owner'] = request.user.id
 form = JournalForm(data, instance=journal)

ここでは、モデルフォームの機能を実際に使用していないことに注意してください。最初のGETでデータディクショナリを明示的に設定する必要はありません。実際、検証をトリガーするため、パラメータを渡す必要はありませんdata。インスタンスとは異なる値を渡す必要がある場合は、initial代わりに使用する必要があります。しかし、ほとんどの場合、通過するだけinstanceで十分です。

また、POSTでは、値を明示的に設定する必要はありません。次のようにするだけです。

journal = form.save()

インスタンスを正しく更新して返します。

于 2010-10-28T09:40:34.210 に答える
2

ここでの哲学は、unique_togetherはORMの概念であり、フォームのプロパティではないということだと思います。特定のフォームにunique_togetherを適用する場合は、独自のクリーンなメソッドを作成できます。これは、簡単で、簡単で、非常に柔軟です。

http://docs.djangoproject.com/en/dev/ref/forms/validation/#cleaning-and-validating-fields-that-depend-on-each-other

これにより、作成したclean_titleメソッドが置き換えられます。

于 2010-10-27T19:48:49.187 に答える