it-swarm-pt.tech

Formulário Django com BooleanField sempre inválido a menos que seja verificado

Eu tenho um aplicativo que usa o seguinte formulário:

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

Eu uso o formulário no seguinte modelo:

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

Minha opinião usa a seguinte estrutura básica de código:

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

Duas coisas estão erradas:

1) Mesmo que eu tenha inicial = True, a caixa de seleção está desmarcada quando a página é carregada

2) O formulário é sempre inválido, a menos que eu marque a caixa de seleção. Fornece erros apenas para up_to_date: "Este campo é obrigatório".

Eu li esta pergunta semelhante mas sua solução não se aplica ao meu projeto.

Então o que está acontecendo?

Editar:

Eu atualizei o código acima para ser mais fiel ao meu código real.

Problema # 1 foi minha culpa porque eu estava substituindo o valor inicial pela vinculação de dados quando o formulário foi instanciado.

Problema # 2 Eu ainda considero um problema. Usar required=False em up_to_date consertará o problema, no entanto, não parece correto usar o widget padrão, uma BooleanField pode ser NULL (faz com que a verificação de validade falhe) ou True, mas nunca False.

43
dhowland

initial=True deve renderizar o widget com checked="checked", então não vejo o problema lá .. mas a razão pela qual o formulário é inválido é porque todos os campos são obrigatórios por padrão, a menos que você especifique o contrário. 

Field.required¶ Por padrão, cada classe Field assume que o valor é Obrigatório, portanto, se você passar um valor vazio - None ou a string Vazia ("") - - então clean () irá gerar uma exceção ValidationError:

Se você quiser que uma caixa de seleção ou qualquer outro valor seja opcional, você precisa passar required=False para o construtor de campo de formulário. 

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

Nos formulários do Django, os campos booleanos devem ser criados com required=False.

Quando a caixa de seleção não está marcada, os navegadores não enviam o campo em POST parâmetros de solicitações. Sem especificar que o campo é opcional, o Django o tratará como um campo ausente quando não estiver nos parâmetros POST.

Por isso, seria legal para o Django ter esse comportamento por padrão para campos de formulário booleanos.

(isso já foi respondido nos comentários de Yuji, mas seria útil como resposta)

30
yairchu

De acordo com documentos oficiais :

Se você quiser incluir um booleano em seu formulário que pode ser True ou False (por exemplo, uma caixa de seleção marcada ou não marcada), você deve se lembrar de passar emrequired=Falseao criar o BooleanField.

11
user

Se você fizer como sugerido, seu up_to_date campo provavelmente será sempre False. E você nunca saberá se isso é porque o usuário quer assim, ou porque o usuário pulou o campo!

Eu tenho o mesmo problema com uma pergunta OPT_IN exigida por lei. Onde, na inscrição, preciso fazer com que o usuário decida conscientemente que meu sistema envie e-mails não solicitados (ou não). Eu estou trabalhando com o Django-Allauth e um banco de dados de usuário personalizado. 

Compartilharei minha solução abaixo na esperança de que você possa adaptá-la à sua situação - adaptei isso de o DOCS .

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

Verifique se você não está sobrescrevendo o formulário inicial na primeira vez que a página é carregada.

Eu originalmente não estava verificando se havia realmente alguma coisa em request.GET. A primeira vez que a página carrega o request.GET é None, e chamar SomeForm (request.GET) acabaria com os valores iniciais.

def some_view(request):

    form = SomeForm()

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

Adicionando um cheque para se certificar de que há algo em request.GET corrigido isso.

def some_view(request):

    form = SomeForm()

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