web-dev-qa-db-de.com

Django-Formular mit BooleanField ist immer ungültig, sofern es nicht aktiviert ist

Ich habe eine Anwendung, die das folgende Formular verwendet:

class ConfirmForm(forms.Form):
    account_name = forms.CharField(widget=forms.HiddenInput)
    up_to_date = forms.BooleanField(initial=True)

Ich verwende das Formular im folgenden Vorlagenauszug:

<form class="confirmform" action="/foo/" enctype="multipart/form-data" method="post">
{{ confirm_form.up_to_date }} Check if this data brings the account up to date.<br>
{{ confirm_form.account_name }} <input type="submit" name="confirm" value="Confirm" />
</form>

Meine Ansicht verwendet die folgende grundlegende Codestruktur:

if request.method == 'POST':
    #check for 'confirm' because I actually have multiple forms in this page
    if 'confirm' in request.POST:
        confirm_form = ConfirmForm(request.POST)
        if confirm_form.is_valid():
            #do stuff
        else:
            c['confirm_form'] = confirm_form
else:
    c['confirm_form'] = ConfirmForm({'account_name':'provided_value'})

Zwei Dinge sind falsch:

1) Auch wenn ich initial = True habe, ist das Kontrollkästchen beim Laden der Seite deaktiviert

2) Das Formular ist immer ungültig, sofern ich das Kontrollkästchen nicht aktiviere. Es gibt nur Fehler für up_to_date: "Dieses Feld ist erforderlich."

Ich habe diese ähnliche Frage gelesen, aber seine Lösung trifft nicht auf mein Projekt zu.

So was ist los?

Bearbeiten:

Ich habe den Code oben aktualisiert, um meinem aktuellen Code treu zu bleiben.

Problem Nr. 1 war mein Fehler, weil ich beim Initialisieren des Formulars den ursprünglichen Wert durch das Binden von Daten überschrieben hatte.

Problem Nr. 2 Ich betrachte immer noch ein Problem. Wenn Sie required=False für up_to_date verwenden, wird das Problem behoben. Es scheint jedoch nicht korrekt zu sein, dass bei Verwendung des Standardwidgets BooleanField entweder NULL (bewirkt, dass die Gültigkeitsüberprüfung fehlschlägt) oder True, aber niemals False.

43
dhowland

initial=True sollte das Widget mit checked="checked" wiedergeben, sodass ich das Problem dort nicht sehe. Der Grund dafür, dass das Formular ungültig ist, ist, dass standardmäßig alle Felder erforderlich sind, sofern Sie nichts anderes angeben. 

Field.required¶ Standardmäßig nimmt jede Field-Klasse an, dass der Wert .__ ist. erforderlich, wenn Sie also einen leeren Wert übergeben - entweder None oder das leere string ("") - dann wird durch clean () eine ValidationError-Ausnahme ausgelöst:

Wenn ein Kontrollkästchen oder ein anderer Wert optional sein soll, müssen Sie required=False an den Formularfeldkonstruktor übergeben. 

up_to_date = forms.BooleanField(initial=True, required=False) 
# no longer required..
35

In Django-Formularen müssen boolesche Felder mit required=False erstellt werden.

Wenn das Kontrollkästchen nicht aktiviert ist, senden Browser das Feld nicht in den POST - Parametern von Anforderungen. Ohne Angabe, dass das Feld optional ist, behandelt Django es als fehlendes Feld, wenn es nicht in den Parametern POST enthalten ist.

Imho wäre es nett für Django, dieses Verhalten standardmäßig für boolesche Formularfelder zu haben.

(Dies wurde bereits in Kommentaren von Yuji beantwortet, wäre aber als Antwort nützlich)

30
yairchu

Nach offiziellen Dokumenten :

Wenn Sie einen Booleschen Wert in Ihr Formular aufnehmen möchten, der entweder True oder False sein kann (z. B. ein angekreuztes oder ungeprüftes Kontrollkästchen), müssen Sie beim Erstellen des BooleanFields required=False übergeben.

11
user

Nur eine Vermutung, aber es könnte sich um die Grundlagen handeln - ein paar Stunden verloren, bis klar wurde, dass es sich bei etwas Ähnlichem nur um grundlegende Variablentypen handelt.

In meinem Fall nach dem Erstellen des Formulars (sehr einfach, siehe unten)

class para_selection(forms.Form):
    first=forms.BooleanField(initial=False, required=False)
    second=forms.BooleanField(initial=False, required=False)
    third=forms.BooleanField(initial=False, required=False)
    fourth=forms.BooleanField(initial=False, required=False)

... Ich habe aus meiner Sicht if selection == 'True': .. verwendet. Da das Formular jedoch Boolesche Werte verwendet, lautet der richtige Code: Wenn die Auswahl True lautet:

Die erste Zeile wird niemals funktionieren, da "==" nicht mit dem Booleschen Typ verwendet wird. Hoffentlich war dies nicht der Fall, aber ich wollte hier darauf hinweisen, da ich ständig im Internet gesucht habe, ohne auf die Art der Variablen zu achten, die ich verwendet habe.

0
Adrian Stanica

Wenn Sie wie vorgeschlagen vorgehen, ist das Feld up_to_date wahrscheinlich immer False. Und Sie werden nie erfahren, ob das so ist, weil der Benutzer es so will, oder, weil der Benutzer über das Feld übersprungen!

Ich habe das gleiche Problem mit einer gesetzlich geforderten OPT_IN-Frage. Bei der Anmeldung muss ich den Benutzer dazu bringen, eine Gewissensentscheidung zu treffen, damit mein System ihnen unerwünschte E-Mails senden kann (oder nicht). Ich arbeite mit Django-Allauth und einer benutzerdefinierten Benutzerdatenbank. 

Ich teile meine Lösung unten in der Hoffnung, dass Sie sie an Ihre Situation anpassen können - ich habe diese von der DOCS angepasst.

forms.py:

# ExtraFieldsSignup as per https://stackoverflow.com/a/12308807
class SignupFormExtraFields(forms.Form):
  CHOICES = (('1', 'YES, keep in touch',), ('2', 'No',))
  first_name = forms.CharField(max_length=30, label='Voornaam')
  last_name = forms.CharField(max_length=30, label='Achternaam')
  opt_in = forms.ChoiceField(widget=forms.RadioSelect, choices=CHOICES)

  def signup(self, request, user):
    user.first_name = self.cleaned_data['first_name']
    user.last_name = self.cleaned_data['last_name']
    # Now we figure out what the user chose:
    if self.cleaned_data['opt_in'] == '1':
        user.opt_in = True
    else:
        user.opt_in = False
    user.save()
0
Richard Cooke

Stellen Sie sicher, dass Sie das ursprüngliche Formular nicht beim ersten Laden der Seite überschreiben.

Ich habe ursprünglich nicht nachgefragt, ob tatsächlich etwas in request.GET war. Das erste Mal, wenn request.GET die Seite lädt, ist None. Beim Aufruf von SomeForm (request.GET) werden die ursprünglichen Werte tatsächlich gelöscht.

def some_view(request):

    form = SomeForm()

    if request.method == 'GET':
        form = SomeForm(request.GET)

Durch das Hinzufügen einer Prüfung, ob etwas in request.GET ist, wurde dieses Problem behoben.

def some_view(request):

    form = SomeForm()

    if request.method == 'GET' and request.GET:
        form = SomeForm(request.GET)
0