Trabalhando com formulários

Sobre este documento

Este documento fornece uma introdução ao básico de formulários web e como eles são manipulados no Django. Para mais detalhes olhe nas áreas específica da API de formulários, veja The Forms API, Form fields, e Form and field validation.

A menos que você planeje construir websites e aplicações que não façam outra coisa além de publicar conteúdo, e não aceita entrada de seus visitantes, você irá precisar entender e usar formulários.

O Django fornece uma gama de ferramentas e bibliotecas para auxiliar a construção de formulários para entrada de dados vindos dos seus visitantes, e então os processa e responde àquela entrada.

Formulários HTML

Em HTML, um formulário é uma coleção de elementos dentro de <form>...</form> que permitem que os visitantes façam coisas como escrever um texto, selecionar opções, manipular objetos ou controle e assim por diante, e então enviar esta informação de volta ao servidor.

Alguns destes elementos da interface do formulário - entrada de texto ou “checkboxes”- são bem simples e fazem parte do próprio HTML. Outros são bem mais complexos; uma interface que faz saltar um seletor de datas ou permite mover uma barra ou manipular controles irá usa geralmente javascript e CSS bem como elementos <input> de formulários HTML para tornar possíveis estes efeitos.

Assim como seus elementos <input>, um formulário precisa especificar duas coisas:

  • onde: a URL para a qual o dado correspondente à entrada do usuário deve ser enviado.
  • como: o método HTTP pelo qual o dado deve ser retornado

Como um exemplo, o formulário de autenticação do admin do Django contém vários elementos do tipo <input>: um type="text" para o nome do usuário, um de type="password" para a senha, e um type="submit" par ao botão “Log in”. Ele também contém alguns objetos escondidos do tipo texto que o usuário não vê, o qual o Django usa para determinar o que fazer depois.

Ele também dia ao navegador web que os dados do formulário devem ser enviados para a URL especificada no atributo <action> do <form> - /admin/ - e isso deve ser enviado usando o mecanismo HTTP especificado pelo atributo method - post.

Quando o elemento <input type="submit" value="Log in"> e disparado, os dados são retornados para /admin/.

GET e POST

GET e POST são os únicos métodos HTTP a se usar enquanto lida com formulários.

O formulário de autenticação do Django é retornado usando o método POST, no qual o navegador web empacota os dados do formulário, codifica-os para transmissão, envia para o servidor, e então recebe de volta sua resposta.

O GET, em contraste, empacota os dados enviados em uma string, e usa isso para compor a URL. A URL contém o endereço para onde o dado deve ser enviado, bem como as chaves dos dados e valore. Você pode ver isso em ação se você fizer uma busca na documentação do Django, a qual irá produzir uma URL na forma de https://docs.djangoproject.com/search/?q=forms&release=1.

GET e POST são tipicamente usados para propósitos distintos.

Qualquer requisição que pode ser usada para alterar o estado do sistema - por exemplo, uma requisição que faz alterações no banco de dados - deve usar POST. O GET deve ser usado somente para requisições que não afetam o estado do sistema.

O GET também não é adequado para formulários com senhas, porque a senha apareceria na URL, e portanto, também no histórico do navegador web e logs do servidor, tudo em texto puro. Nem mesmo seria adequado para grandes quantidade de dados, ou para dados binários, como uma imagem. Uma aplicação web que usa as requisições to tipo GET para formulários de administração é um risco de segurança: pode ser fácil para um ataque imitar uma requisição de um formulário para ter acesso a partes sensíveis do sistema. O POST juntamente com outras proteções como a proteção CSRF do Django oferece mais controle sobre o acesso.

Por outro lado, o GET é adequado para coisas como um formulário web de busca, porque a URL que represente uma requisição do tipo GET pode ser facilmente adicionado a lista de favoritos, comparilhada ou reenviada.

Regras do Django em formulários

Lidar com formulários é uma coisa complexa. Considere o admin do Django, onde inúmeros itens de dados de diferentes tipos precisam ser preparados para serem visualizados em um formulário, renderizados como HTML, editado usando uma interface conveniente, retornados ao servidor, validados e limpos, e então salvos ou passados adiante para mais processamento.

A funcionalidade de formulários do Django pode simplificar e automatizar uma boa parte deste trabalho, e pode também fazer isso de maneira mais segura que a maioria dos programadores poderiam ser capazes de fazer no código que escrevem.

O Django lida com três partes distintas do trabalho envolvendo formulários:

  • Preparando e reestruturando os dados para torná-los prontos para rederização
  • criando formulários HTML para os dados
  • recebendo e processando formulário enviado e os dados vindos do cliente

É possível escrever código que faz tudo isso manualmente, mas o Django pode tomar conta de tudo isso pra você.

Formulários no Django

Descrevemos brevemente os formulários HTML, mas um <form> HTML é apenas uma parte do maquinário requerido.

Dentro do contexto de uma aplicação web, um ‘formulário’ talvez se refira ao <form> HTML, ou a Form do Django que o produz, ou ao dado estruturado retornado quando este é enviado, ou ao conjunto funcional de todas essas partes.

A classe Form do Django

No coração deste sistema de componentes está a classe Form do Django. Mais ou menos como o modelo do Django descreve a estrutura lógica de um objeto, seu comportamento, e a maneira como suas partes são apresentadas para nós, uma classe Form descreve um formulário e determina como este funciona e aparece.

De maneira similar que a classe de modelo mapeia os campos para o banco de dados, uma classe de formulário mapeia para os elementos <input> do formulário HTML. (Uma ModelForm mapeia os campos de uma classe de modelo para elementos <input> do formulário HTML através da Form; o admin do Django é baseado nisso.)

Os campos dos formulários são eles próprios classes; eles manipulam dados do formulário e fazem validações quando o formulário é enviado. A DateField e a FileField lidam com tipos de dados muito diferentes e tem que fazer coisas diferentes com isso.

Um campo de formulário, para o usuário, é representado como um “widget” HTML - um pedaço do maquinário de interface do usuário. Cada campo tem uma classe de Widget padrão, mas isso pode ser sobrescrito quando requerido.

Instanciando, processando, e renderizando formulários

Quando renderizar um objeto no Django, em geral:

  1. pegamos o objeto na “view” (lido no banco de dados, por exemplo)
  2. o passamos para o “context” do template
  3. expandimos para a marcação HTML usando variáveis de template

Mostrar um formulário em um template envolve quase o mesmo trabalho que mostrar qualquer outro tipo de objeto, mas existem algumas diferenças chave.

No caso de uma instância de modelo que não contém nenhum dado, é raramente útil ou talvez nunca seja útil para fazer nada com isso no template. Por outro lado, faz todo o sentido, mostrar um formulário vazio - que é o que fazemos quando queremos que o usuário o preencha.

Então quando lidamos com uma instância de modelo em uma “view”, nós tipicamente a recuperamos do banco de dados. Quando lidamos com um formulário, nós tipicamente o instanciamos na “view”.

Quando nós instanciamos um formulário, nós podemos optar por deixá-lo vazio ou o pré-preenchemos, por exemplo com:

  • dados vindos de uma instância de modelo salva previamente (como no caso dos formulários do admin para edição)
  • dados que tenhamos coletado de outras fontes
  • dados recebidos de uma prévia submissão de formulário

O último destes casos é o mais interessante, porque é o que torna possível para um usuário não somente ler um site web, mas enviar informação de volta a ele.

Construindo um formulário

O trabalho que tem que ser feito

Suponha queira criar um formulário simples no seu site web, de modo a obter o nome do usuário. Você precisaria de algo como isso aqui no seu template:

<form action="/your-name/" method="post">
    <label for="your_name">Your name: </label>
    <input id="your_name" type="text" name="your_name" value="{{ current_name }}">
    <input type="submit" value="OK">
</form>

Isso diz ao navegador web para retornar os dados do form para a URL /your-name/, usando o métodos POST. Ele irá mostrar um campo, chamado “Your name:”, e um botão com um “OK”. Se o “context” do template contiver uma variável chamada current_name, esta será usada para pré-preencher o campo your_name.

Você irá precisar uma “view” que renderiza o template contendo o formulário HTML, e que pode fornecer o campo current_name apropriadamente.

Quando o formulário é submetido, a requisição do tipo POST a qual é enviada ao servidor irá conter os dados do formulário.

Agora você irá também precisar de uma “view” que corresponda aquela URL /your-name/ a qual irá encontrar o par chave/valor apropriado na requisição e então processá-lo.

Este é um formulário muito simples. Na prática, um formulário pode conter dezenas ou centenas de campos, muitos dos quais talvez precisem ser pré-preenchidos, e talvez nós esperamos que o usuário trabalhe através do ciclo de edição e submissão muitas vezes antes de concluir a operação.

Nós podemos requerer que alguma validação ocorra no navegador web, mesmo antes do formulário ser submetido; nós podemos querer usar campos bem mais complexos, que possibilite ao usuário selecionar uma data de um calendário e assim por diante.

Neste ponto é muito mais fácil ter o Django fazendo a maior parte deste trabalho para nós.

Construindo um formulário no Django

A classe Form

Nós já sabemos como queremos que o nosso formulário HTML se pareça. Nosso ponto de início para isso no Django é este:

forms.py
from django import forms

class NameForm(forms.Form):
    your_name = forms.CharField(label='Your name', max_length=100)

Este código define uma classe Form com um único campo (your_name). Nós aplicamos uma etiqueta ao campo mais legível a seres humanos, a qual irá aparecer na <label> quando for renderizado (embora neste caso o label que especificamos é o mesmo que seria gerado automaticamente se o tivéssemos omitido).

O tamanho máximo do conteúdo do campo é definido pelo max_length. Isso faz duas coisas. Define a maxlength="100" no <input> HTML (então o navegador web impedirá que o usuário entre com um número de caracteres maio que aquele definido). Isso também significa que quando o Django receber o form de volta do navegador, o Django validará o comprimento do dado.

Uma instância da Form tem um método is_valid(), o qual executa rotinas de validações para todos os campos. Quando este método é chamado, se todos os campos contiverem dados válidos, ele irá:

  • retornar True
  • colocar os dados do formulário no atributo cleaned_data do formulário.

O formulário, quando renderizado pela primeira vez, se parecerá com:

<label for="your_name">Your name: </label>
<input id="your_name" type="text" name="your_name" maxlength="100" required>

Repare que não estão incluídas as tags <form>, ou o botão de submit. Teremos que fornecê-los nós mesmos no template.

A “view”

Os dados de formulários enviados para um site Django são processados por uma “view”, geralmente a mesma “view” que publica o formulário. Isso nos permiti reutilizar parte da mesma lógica.

Para lidar com o formulário precisamos instanciá-lo na “view” da URL onde queremos que seja publicado:

views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render

from .forms import NameForm

def get_name(request):
    # if this is a POST request we need to process the form data
    if request.method == 'POST':
        # create a form instance and populate it with data from the request:
        form = NameForm(request.POST)
        # check whether it's valid:
        if form.is_valid():
            # process the data in form.cleaned_data as required
            # ...
            # redirect to a new URL:
            return HttpResponseRedirect('/thanks/')

    # if a GET (or any other method) we'll create a blank form
    else:
        form = NameForm()

    return render(request, 'name.html', {'form': form})

Se chegarmos a essa “view” com uma requisição do tipo GET, ela irá criar uma instância vazia de formulário e colocá-lo no “context” do template para renderizá-lo. Isso é o que esperamos que aconteça quando visitarmos a URL pela primeira vez.

Se o formulário for submetido usando uma requisição do tipo POST, a “view” irá mais uma vez criar uma instância do formulário e preenchê-la com dados vindos da requisição form = NameForm(request.POST). Isso é chamado “carregar dados no formulário” (ele é agora um “bound form”).

Chamamos o método is_valid()``do formulário; se não é ``True, nós retornamos ao template com o formulário. Desta vez o formulário não está mais vazio (unbound) então o formulário HTML será preenchido com os dados previamente submetidos, onde podem ser editados e corrigidos quando necessário.

Se is_valid() é True, seremos capazes de encontrar todos os dados do formulário no atributo cleaned_data. Podemos usar estes dados para atualizar o banco de dados ou fazer outro processamento antes de enviar um redirecionamento HTTP para o navegador dizendo onde ir a seguir.

O template

Nós não precisamos fazer muito no nosso template name.html. O exemplo mais simples é:

<form action="/your-name/" method="post">
    {% csrf_token %}
    {{ form }}
    <input type="submit" value="Submit">
</form>

Todos os campos dos formulário e seus atributos serão desempacotados dentro da marcação HTML vindos do {{ form }} através da linguagem de templates do Django.

Formulários e a proteção contra “Falsificação de solicitação entre sites”

O Django traz uma proteção contra falsificação de solicitação entre sites (CSRF em Inglês). Quando se submete um formulário através de um POST com a proteção CSRF habilitada você deve usar a template tag csrf_token como no exemplo anterior. Porém, uma vez que a proteção CSRF não está diretamente ligada a formulários em templates, essa tag é omitida dos próximos exemplos neste documento.

Tipos de entradas HTML5 e validação do navegador

Se o seu formulário inclui uma URLField, um EmailField ou qualquer tipo de campo do tipo inteiro, o Django irá usar o tipos de entradas HTML5 url, email e number. Por padrão, os navegadores web podem aplicar suas próprias validações nestes campos, as quais podem ser mais rigorosas do que a validação do Django. Se você quiser desabilitar este comportamento, defina o atributo novalidate na tag do form, ou especifique um “widget” diferente para o campo, como uma TextInput.

Nós agora temos um formulário web funcional, descrito pela Form do Django, processado pela “view”, e renderizado como um <form> HTML.

Isso é tudo o que você precisa para iniciar, mas o framework de formulários lhe dá muito mais. Uma vez que tenha entendido o básico dos processos descritos acima, você deve estar preparado para entender outras características do sistema de formulários e pronto par aprender um pouco mais sobre maquinário por de trás disso.

Mais sobre as classes de Form do Django

All form classes are created as subclasses of either django.forms.Form or django.forms.ModelForm. You can think of ModelForm as a subclass of Form. Form and ModelForm actually inherit common functionality from a (private) BaseForm class, but this implementation detail is rarely important.

Modelos e Formulários

In fact if your form is going to be used to directly add or edit a Django model, a ModelForm can save you a great deal of time, effort, and code, because it will build a form, along with the appropriate fields and their attributes, from a Model class.

Bound and unbound form instances

The distinction between Bound and unbound forms is important:

  • An unbound form has no data associated with it. When rendered to the user, it will be empty or will contain default values.
  • A bound form has submitted data, and hence can be used to tell if that data is valid. If an invalid bound form is rendered, it can include inline error messages telling the user what data to correct.

The form’s is_bound attribute will tell you whether a form has data bound to it or not.

More on fields

Consider a more useful form than our minimal example above, which we could use to implement “contact me” functionality on a personal website:

forms.py
from django import forms

class ContactForm(forms.Form):
    subject = forms.CharField(max_length=100)
    message = forms.CharField(widget=forms.Textarea)
    sender = forms.EmailField()
    cc_myself = forms.BooleanField(required=False)

Our earlier form used a single field, your_name, a CharField. In this case, our form has four fields: subject, message, sender and cc_myself. CharField, EmailField and BooleanField are just three of the available field types; a full list can be found in Form fields.

Widgets

Each form field has a corresponding Widget class, which in turn corresponds to an HTML form widget such as <input type="text">.

In most cases, the field will have a sensible default widget. For example, by default, a CharField will have a TextInput widget, that produces an <input type="text"> in the HTML. If you needed <textarea> instead, you’d specify the appropriate widget when defining your form field, as we have done for the message field.

Dados do campo

Whatever the data submitted with a form, once it has been successfully validated by calling is_valid() (and is_valid() has returned True), the validated form data will be in the form.cleaned_data dictionary. This data will have been nicely converted into Python types for you.

Nota

You can still access the unvalidated data directly from request.POST at this point, but the validated data is better.

In the contact form example above, cc_myself will be a boolean value. Likewise, fields such as IntegerField and FloatField convert values to a Python int and float respectively.

Here’s how the form data could be processed in the view that handles this form:

views.py
from django.core.mail import send_mail

if form.is_valid():
    subject = form.cleaned_data['subject']
    message = form.cleaned_data['message']
    sender = form.cleaned_data['sender']
    cc_myself = form.cleaned_data['cc_myself']

    recipients = ['info@example.com']
    if cc_myself:
        recipients.append(sender)

    send_mail(subject, message, sender, recipients)
    return HttpResponseRedirect('/thanks/')

Dica

For more on sending email from Django, see Enviando e-mail.

Some field types need some extra handling. For example, files that are uploaded using a form need to be handled differently (they can be retrieved from request.FILES, rather than request.POST). For details of how to handle file uploads with your form, see Binding uploaded files to a form.

Working with form templates

All you need to do to get your form into a template is to place the form instance into the template context. So if your form is called form in the context, {{ form }} will render its <label> and <input> elements appropriately.

Form rendering options

Additional form template furniture

Don’t forget that a form’s output does not include the surrounding <form> tags, or the form’s submit control. You will have to provide these yourself.

There are other output options though for the <label>/<input> pairs:

  • {{ form.as_table }} will render them as table cells wrapped in <tr> tags
  • {{ form.as_p }} will render them wrapped in <p> tags
  • {{ form.as_ul }} will render them wrapped in <li> tags

Note that you’ll have to provide the surrounding <table> or <ul> elements yourself.

Here’s the output of {{ form.as_p }} for our ContactForm instance:

<p><label for="id_subject">Subject:</label>
    <input id="id_subject" type="text" name="subject" maxlength="100" required></p>
<p><label for="id_message">Message:</label>
    <textarea name="message" id="id_message" required></textarea></p>
<p><label for="id_sender">Sender:</label>
    <input type="email" name="sender" id="id_sender" required></p>
<p><label for="id_cc_myself">Cc myself:</label>
    <input type="checkbox" name="cc_myself" id="id_cc_myself"></p>

Note that each form field has an ID attribute set to id_<field-name>, which is referenced by the accompanying label tag. This is important in ensuring that forms are accessible to assistive technology such as screen reader software. You can also customize the way in which labels and ids are generated.

See Outputting forms as HTML for more on this.

Rendering fields manually

We don’t have to let Django unpack the form’s fields; we can do it manually if we like (allowing us to reorder the fields, for example). Each field is available as an attribute of the form using {{ form.name_of_field }}, and in a Django template, will be rendered appropriately. For example:

{{ form.non_field_errors }}
<div class="fieldWrapper">
    {{ form.subject.errors }}
    <label for="{{ form.subject.id_for_label }}">Email subject:</label>
    {{ form.subject }}
</div>
<div class="fieldWrapper">
    {{ form.message.errors }}
    <label for="{{ form.message.id_for_label }}">Your message:</label>
    {{ form.message }}
</div>
<div class="fieldWrapper">
    {{ form.sender.errors }}
    <label for="{{ form.sender.id_for_label }}">Your email address:</label>
    {{ form.sender }}
</div>
<div class="fieldWrapper">
    {{ form.cc_myself.errors }}
    <label for="{{ form.cc_myself.id_for_label }}">CC yourself?</label>
    {{ form.cc_myself }}
</div>

Complete <label> elements can also be generated using the label_tag(). For example:

<div class="fieldWrapper">
    {{ form.subject.errors }}
    {{ form.subject.label_tag }}
    {{ form.subject }}
</div>

Rendering form error messages

Of course, the price of this flexibility is more work. Until now we haven’t had to worry about how to display form errors, because that’s taken care of for us. In this example we have had to make sure we take care of any errors for each field and any errors for the form as a whole. Note {{ form.non_field_errors }} at the top of the form and the template lookup for errors on each field.

Using {{ form.name_of_field.errors }} displays a list of form errors, rendered as an unordered list. This might look like:

<ul class="errorlist">
    <li>Sender is required.</li>
</ul>

The list has a CSS class of errorlist to allow you to style its appearance. If you wish to further customize the display of errors you can do so by looping over them:

{% if form.subject.errors %}
    <ol>
    {% for error in form.subject.errors %}
        <li><strong>{{ error|escape }}</strong></li>
    {% endfor %}
    </ol>
{% endif %}

Non-field errors (and/or hidden field errors that are rendered at the top of the form when using helpers like form.as_p()) will be rendered with an additional class of nonfield to help distinguish them from field-specific errors. For example, {{ form.non_field_errors }} would look like:

<ul class="errorlist nonfield">
    <li>Generic validation error</li>
</ul>

See The Forms API for more on errors, styling, and working with form attributes in templates.

Looping over the form’s fields

If you’re using the same HTML for each of your form fields, you can reduce duplicate code by looping through each field in turn using a {% for %} loop:

{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
        {% if field.help_text %}
        <p class="help">{{ field.help_text|safe }}</p>
        {% endif %}
    </div>
{% endfor %}

Useful attributes on {{ field }} include:

{{ field.label }}
The label of the field, e.g. Email address.
{{ field.label_tag }}

The field’s label wrapped in the appropriate HTML <label> tag. This includes the form’s label_suffix. For example, the default label_suffix is a colon:

<label for="id_email">Email address:</label>
{{ field.id_for_label }}
The ID that will be used for this field (id_email in the example above). If you are constructing the label manually, you may want to use this in lieu of label_tag. It’s also useful, for example, if you have some inline JavaScript and want to avoid hardcoding the field’s ID.
{{ field.value }}
The value of the field. e.g someone@example.com.
{{ field.html_name }}
The name of the field that will be used in the input element’s name field. This takes the form prefix into account, if it has been set.
{{ field.help_text }}
Any help text that has been associated with the field.
{{ field.errors }}
Outputs a <ul class="errorlist"> containing any validation errors corresponding to this field. You can customize the presentation of the errors with a {% for error in field.errors %} loop. In this case, each object in the loop is a simple string containing the error message.
{{ field.is_hidden }}
This attribute is True if the form field is a hidden field and False otherwise. It’s not particularly useful as a template variable, but could be useful in conditional tests such as:
{% if field.is_hidden %}
   {# Do something special #}
{% endif %}
{{ field.field }}
The Field instance from the form class that this BoundField wraps. You can use it to access Field attributes, e.g. {{ char_field.field.max_length }}.

Ver também

For a complete list of attributes and methods, see BoundField.

Looping over hidden and visible fields

If you’re manually laying out a form in a template, as opposed to relying on Django’s default form layout, you might want to treat <input type="hidden"> fields differently from non-hidden fields. For example, because hidden fields don’t display anything, putting error messages “next to” the field could cause confusion for your users – so errors for those fields should be handled differently.

Django provides two methods on a form that allow you to loop over the hidden and visible fields independently: hidden_fields() and visible_fields(). Here’s a modification of an earlier example that uses these two methods:

{# Include the hidden fields #}
{% for hidden in form.hidden_fields %}
{{ hidden }}
{% endfor %}
{# Include the visible fields #}
{% for field in form.visible_fields %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    </div>
{% endfor %}

This example does not handle any errors in the hidden fields. Usually, an error in a hidden field is a sign of form tampering, since normal form interaction won’t alter them. However, you could easily insert some error displays for those form errors, as well.

Reusable form templates

If your site uses the same rendering logic for forms in multiple places, you can reduce duplication by saving the form’s loop in a standalone template and using the include tag to reuse it in other templates:

# In your form template:
{% include "form_snippet.html" %}

# In form_snippet.html:
{% for field in form %}
    <div class="fieldWrapper">
        {{ field.errors }}
        {{ field.label_tag }} {{ field }}
    </div>
{% endfor %}

If the form object passed to a template has a different name within the context, you can alias it using the with argument of the include tag:

{% include "form_snippet.html" with form=comment_form %}

If you find yourself doing this often, you might consider creating a custom inclusion tag.