.. .. META INFORMATION OF TRANSLATION .. .. $TranslationStatus: Done, waiting for revision $ .. $OriginalRevision: 11332 $ .. $TranslationAuthors: Robson Mendonça $ .. .. INFO OF THIS FILE (DO NOT EDIT! UPDATED BY SUBVERSION) .. .. $HeadURL$ .. $LastChangedRevision$ .. $LastChangedBy$ .. $LastChangedDate$ .. .. _ref-contrib-formtools-form-wizard: =========== Form wizard =========== .. module:: django.contrib.formtools.wizard :synopsis: Divide formulários em múltiplas páginas Web. O Django vem acompanhado uma aplicação opicional "form wizard" que divide :doc:`formulários ` em várias páginas Web. Ele mantém o estado num hash HTML de campos :samp:``, e os dados não são processados no lado do servidor até que a última parte do formulário seja submetida. Você pode querer usar isto com formulários muito compridos que seriam desagradáveis de se mostrar numa única página. A primeira página pode pedir ao usuário informações centrais, a segunda coisas menos importantes, e assim por diante. o termo "wizard" (em português "assistente") neste contexto, é `explicado na Wikipedia`_. .. _explicado na Wikipedia: http://en.wikipedia.org/wiki/Wizard_%28software%29 .. _forms: ../forms/ Como ele funciona ================= Aqui temos o fluxo de funcionamento básico de como um usuário poderia usar um wizard: 1. O usuário visita a primeira página do wizard, preenche o formulário e o submete. 2. O servidor valida os dados. Se eles forem inválidos, o formulário é mostrado novamente, com mensagens de erro. Se for válido, o servidor calcula um hash seguro dos dados e apresenta ao usuário o próximo formulário, poupando os dados validados e o hash em campos :samp:``. 3. Repete o passo 1 e 2, para todos os formulários subsequentes do wizard. 4. Um vez que o usuário tenha submetido todos os formulários e todos os dados estejam validados, o wizard processa os dados -- salvando-os no banco de dados, enviando um e-mail, ou seja lá o que for que a aplicação precise fazer. Uso === Esta aplicação manipula tantos mecanismos para você quanto possível. Geralmente, você só tem que fazer estas coisas: 1. Definir um número de classes :class:`~django.forms.Form` -- uma por página do wizard. 2. Criar uma classe :class:`FormWizard` que especifica o que fazer uma vez que todos os seus formulários foram submetidos e validados. Isso também permite você sobrescrever alguns comportamentos do wizard. 3. Criar alguns templates que renderizam formulários. Você pode definir um único template genérico para manipular todos os formulários, ou definir um template específico para cada formulário. 4. Apontar seu URLconf para sua classe :class:`FormWizard`. Definindo classes ``Form`` ========================== o primeiro passo para criar um "form wizard" é criar as classes :class:`~django.forms.forms.Form`. Estes devel ser classses :class:`django.forms.Form` padrão conforme explicado na :doc:`documentação do forms `. Estas classes podem estar em qualquer lugar de sua base de código, mas por convenção são postas num arquivo chamado :file:`forms.py` dentro da sua aplicação. Por exemplo, vamos escrever um wizard "contact form", onde a primeira página coleta o endereço de email do remetente e o assunto, e a segunda página coleta a mensagem em si. Aqui tem um exemplo de como o :file:`forms.py` pode ficar:: from django import forms class ContactForm1(forms.Form): subject = forms.CharField(max_length=100) sender = forms.EmailField() class ContactForm2(forms.Form): message = forms.CharField(widget=forms.Textarea) **Limitação importante:** Pelo wizard usar campos HTML hidden para armazenar dados entre as páginas, você não pode incluir um :class:`~django.forms.fields.FileField` em qualquer outra página, a não ser na última. Criando uma classe ``FormWizard`` ================================= O próximo passo é criar uma classe :class:`~django.contrib.formtools.wizard.FormWizard`, que deveria ser uma subclasse do ``django.contrib.formtools.wizard.FormWizard``. Assim como suas classes :class:`~django.forms.forms.Form`, esta classe :class:`~django.contrib.formtools.wizard.FormWizard` pode estar em qualquer lugar da sua base de código, mas por convenção é colocado no :file:`forms.py`. O único requerimento desta subclasse é que ela implementa um método :meth:`~django.contrib.formtools.wizard.FormWizard.done()`, que especifica o que deve acontecer quando *todos* os formulários foram submetidos e validados. A este método é passado dois argumentos: * ``request`` -- um objeto :class:`~django.http.HttpRequest` * ``form_list`` -- uma lista de classes :mod:`django.forms` :class:`~django.forms.forms.Form` Neste exemplo simplista, ao invés de executar qualquer operação de banco de dados, o método simplesmente renderiza um template com os dados validados:: from django.shortcuts import render_to_response from django.contrib.formtools.wizard import FormWizard class ContactWizard(FormWizard): def done(self, request, form_list): return render_to_response('done.html', { 'form_data': [form.cleaned_data for form in form_list], }) Note que este método será chamado via ``POST``, então ele realmente deveria ser um bom cidadão Web e redirecionar depois de processar os dados. Aqui tem outro exemplo:: from django.http import HttpResponseRedirect from django.contrib.formtools.wizard import FormWizard class ContactWizard(FormWizard): def done(self, request, form_list): do_something_with_the_form_data(form_list) return HttpResponseRedirect('/page-to-redirect-to-when-done/') Veja a seção `Métodos avançados do FormWizard`_ abaixo para aprender mais sobre os hooks do :class:`~django.contrib.formtools.wizard.FormWizard`. Criando templates para formulários ================================== Agora, precisamos criar um template que renderize os forumlários do wizard. Por padrão, todo formulário usa um template chamado :file:`forms/wizard.html`. (Você pode mudar este nome sobrescrevendo o método :meth:`~django.contrib.formtools.wizard..get_template()`, que está documentado abaixo. Este hook também permite você usar um template diferente para cada formulário.) Este template aguarda o seguinte contexto: * ``step_field`` -- O nome do campo hidden contento o passo. * ``step0`` -- O passo atual (partindo de zero). * ``step`` -- O passo atual (partindo de um). * ``step_count`` -- O total de passos. * ``form`` -- A instância do :class:`~django.forms.forms.Form` para o passo atual (vazio ou com erros). * ``previous_fields`` -- Uma string representando todos os campos de dados anteriores, mais os hashes de formulários completados, todos em campos hidden de formulário. Note que você precisará executar isto através do template filter :meth:`~django.template.defaultfilters.safe`, para previnir o auto escape, por ele é um HTML puro. Ele também será transmitido a quaisquer objetos no :data:`extra_context`, que é um dicionário que você pode especificar contendo valores extra a serem adicionados ao contexto. Você pode especificá-lo de duas formas: * Sete o atributo :attr:`~django.contrib.formtools.wizard.FormWizard.extra_context` na sua subclasse :class:`~django.contrib.formtools.wizard.FormWizard` para um dicionário. * Passe :attr:`~django.contrib.formtools.wizard.FormWizard.extra_context` como parâmetros extra no URLconf. Aqui tem um exemplo de template completo: .. code-block:: html+django {% extends "base.html" %} {% block content %}

Step {{ step }} of {{ step_count }}

{{ form }}
{{ previous_fields|safe }}
{% endblock %} Note que ``previous_fields``, ``step_field`` e ``step0`` são todos obrigatórios para o wizard funcionar perfeitamente. Ligando o wizard num URLconf ============================ Finalmente, dê ao seu novo objeto :class:`~django.contrib.formtools.wizard.FormWizard` uma URL no ``urls.py``. O wizard recebe uma lista de seus objetos form como argumentos:: from django.conf.urls.defaults import * from mysite.testapp.forms import ContactForm1, ContactForm2, ContactWizard urlpatterns = patterns('', (r'^contact/$', ContactWizard([ContactForm1, ContactForm2])), ) Métodos avançados do FormWizard =============================== .. class:: FormWizard Além do método :meth:`~django.contrib.formtools.wizard.FormWizard.done()`, o :class:`~django.contrib.formtools.wizard.FormWizard` oferece alguns métodos avançados que permitem você customizar o funcionamento do seu wizard. Alguns destes métodos recebem um argumento ``step``, que é um contador, partindo de zero, representando o passo corrente do wizard. (E.g., o primeiro form é ``0`` e o segundo form é ``1``.) .. method:: FormWizard.prefix_for_step Dado um passo, retorna um prefixo de :class:`~django.forms.forms.Form` para uso. Por padrão, este simplesmente usa o passo em si. Para mais, veja a :ref:`documentação do prefixo de formulário `. Implementação padrão:: def prefix_for_step(self, step): return str(step) .. method:: FormWizard.render_hash_failure Renderiza um template se a verificação do hash falhar. É raro você precisar sobrescrever este método. Implementação padrão:: def render_hash_failure(self, request, step): return self.render(self.get_form(step), request, step, context={'wizard_error': 'Desculpe-nos, mas seu formulário expirou. Por favor continue preenchendo o formulário desta página.'}) .. method:: FormWizard.security_hash Calcula o hash de segurança para um dado objeto request e instância de :class:`~django.forms.forms.Form`. Por padrão, ele usa um hash MD5 dos dados do formulário e sua configuração :setting:`SECRET_KEY`. É raro alguém precisar sobrescrever isso. Exemplo:: def security_hash(self, request, form): return my_hash_function(request, form) .. method:: FormWizard.parse_params Um hook para salvar o estado do objeto request e ``args`` / ``kwargs`` que foram capiturados da URL pelo seu URLconf. Por padrão, isso não faz nada. Exemplo:: def parse_params(self, request, *args, **kwargs): self.my_state = args[0] .. method:: FormWizard.get_template Retorna o nome do template que deveria ser usado para um certo passo. Por padrão, ele retorna :file:`'forms/wizard.html'`, indiferente do passo. Exemplo:: def get_template(self, step): return 'myapp/wizard_%s.html' % step Se o :meth:`~FormWizard.get_template` retorna uma lista de strings, então o wizard usará a função do sistema de template :func:`~django.template.loader.select_template()`, :ref:`explanada na documentação de template `. Isto significa que o sistema usará o primeiro template que existir no sistema de arquivo. Por exemplo:: def get_template(self, step): return ['myapp/wizard_%s.html' % step, 'myapp/wizard.html'] .. _explanado na documentação de template: ../templates_python/#the-python-api .. method:: FormWizard.render_template Renderiza o template para o dado passo, retornando um objeto :class:`~django.http.HttpResponse`. Sobrescreva este método se você deseja adicionar um contexto customizado, retornar um tipo MIME diferente, etc. Se você somente precisa sobrescrever o nome do template, use o método :meth:`~FormWizard.get_template`. O template será renderizado com o contexto documentado na seção acima "criando templates para formulários". .. method:: FormWizard.process_step Um hook para modificar o estado interno do wizard, dado uma totalidade de objetos :class:`~django.forms.forms.Form` validados. O Form está garantido a ter dados limpos e válidos. Este método *não* deve modificar qualquer dado. Ao invés disso, ele pode querer setar um ``self.extra_context`` ou dinamicamente alterar o ``self.form_list``, baseado nos formulários submetidos anteriormente. Note que este método é chamado toda vez que uma página é renderizada para *todos* os passos submetidos. A assinatura da função:: def process_step(self, request, form, step): # ... Providing initial data for the forms ==================================== .. attribute:: FormWizard.initial Initial data for a wizard's :class:`~django.forms.Form` objects can be provided using the optional :attr:`~FormWizard.initial` keyword argument. This argument should be a dictionary mapping a step to a dictionary containing the initial data for that step. The dictionary of initial data will be passed along to the constructor of the step's :class:`~django.forms.Form`:: >>> from testapp.forms import ContactForm1, ContactForm2, ContactWizard >>> initial = { ... 0: {'subject': 'Hello', 'sender': 'user@example.com'}, ... 1: {'message': 'Hi there!'} ... } >>> wiz = ContactWizard([ContactForm1, ContactForm2], initial=initial) >>> form1 = wiz.get_form(0) >>> form2 = wiz.get_form(1) >>> form1.initial {'sender': 'user@example.com', 'subject': 'Hello'} >>> form2.initial {'message': 'Hi there!'}