Warum nicht Django meiner unique_together Einschränkung als form.ValidationError erzwingen, anstatt eine Ausnahme zu werfen?

StackOverflow https://stackoverflow.com/questions/4034911

Frage

Edit: Während dieser Beitrag ist ein Duplikat Django Modelform unique_together Validierung aus dem Modelform ‚ausschließen‘ die akzeptierte Antwort hier zu entfernen, die in der anderen Frage eine viel sauberere Lösung als die akzeptierte Antwort.

Dies ist ein Follow-up auf diese Frage .

Wenn ich nicht explizit die unique_together Einschränkung in der clean_title () Funktion überprüfen, django löst eine Ausnahme:

  

IntegrityError bei / Zeitschrift / Zeitschrift / 4

     

doppelte Schlüsselwert verletzt eindeutige Einschränkung "journal_journal_owner_id_key"

     

Anfrage Methode: POST

     

Anforderungs-URL: http: // localhost: 8000 / Zeitschrift / Zeitschrift / 4

     

Ausnahmetyp: IntegrityError

     

Ausnahme Wert: doppelte Schlüsselwert verletzt eindeutige Einschränkung "journal_journal_owner_id_key"

     

Ausnahme Ort: /Library/Python/2.6/site-packages/django/db/backends/util.py in ausführen, Zeile 19

Doch ich habe den Eindruck, dass Django diese Einschränkung schön durch Anheben einen Validation erzwingen würde, nicht mit einer Ausnahme, die ich zu fangen müssen.

Unten ist mein Code mit einer zusätzlichen clean_title () Methode I Verwendung als Behelfslösung. Aber ich will wissen, was ich falsch mache, so dass django ist nicht die Einschränkung in der erwarteten Weise durchzusetzen.

Danke.

Modell Code:

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 

Formularcode:

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

Code anzeigen:

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>')

UPDATE ARBEITSCode: Dank Daniel Roseman.

Modell Code bleibt die gleiche wie oben.

Formularcode - Entfernen ausschließen Anweisung und clean_title Funktion:

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

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

Code anzeigen - füge individuellen Einzigartigkeit Fehlermeldung:

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>')
War es hilfreich?

Lösung

Das Problem ist, dass Sie speziell sind mit Ausnahme eines der Felder in der einzigartigen Kontrolle beteiligt ist, und Django wird die Prüfung unter diesen Umständen nicht ausgeführt - vgl. Die _get_unique_checks Methode in Zeile 722 von django.db.models.base

Statt mit Ausnahme des owner Feldes, würde ich halte es einfach verlassen die Vorlage aus und den Wert explizit auf den Daten, die Sie vorbei in über Instanziierung:

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

Beachten Sie, dass Sie nicht wirklich die Kraft des Modelform hier verwendet wird. Sie müssen nicht explizit das Datenwörterbuch auf dem anfänglichen GET gesetzt - und in der Tat, Sie nicht einen data Parameter dort passieren, da es Validierung auslöst: Wenn Sie in Werten übergeben müssen , die auf die Instanz unterschiedlich sind, sollten Sie initial stattdessen verwenden. Aber die meiste Zeit, nur auf der Durch instance ist genug.

Und auf POST, wieder brauchen Sie nicht explizit die Werte zu setzen: Sie können einfach tun:

journal = form.save()

, die die Instanz korrekt aktualisiert und es zurück.

Andere Tipps

Ich denke, die Philosophie hier ist, dass unique_together ein ORM-Konzept ist, nicht eine Eigenschaft eines Formulars. Wenn Sie für eine bestimmte Form erzwingen unique_together möchten, können Sie Ihre eigene saubere Methode schreiben, die einfach, unkompliziert und sehr flexibel:

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

Das wird die clean_title Methode ersetzen Sie geschrieben haben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top