29 de Febrero de 2012 · 3 min de lectura
En este apunte veremos cómo utilizar las generic class views para trabajar con formularios. Utilizaremos el mismo ejemplo que Django utiliza cuando trata de formularios. En la documentación hace referencia a un formulario de contacto creado como:
from django import forms class ContactForm(forms.Form): subject = forms.CharField(max_length=100) message = forms.CharField() sender = forms.EmailField() cc_myself = forms.BooleanField(required=False)
y la vista asociada
def contact(request): if request.method == 'POST': # If the form has been submitted... form = ContactForm(request.POST) # A form bound to the POST data if form.is_valid(): # All validation rules pass # Process the data in form.cleaned_data # ... return HttpResponseRedirect('/thanks/') # Redirect after POST else: form = ContactForm() # An unbound form return render_to_response('contact.html', { 'form': form, })
Lo que haremos es ver cómo podemos tener esto mismo pero utilizando las generic class views. Lo primero es ver qué necesitamos: necesitamos gestionar un formulario, su validación y enviar la redirección hacia una nueva página web en caso de que todo vaya bien, es decir, que se pase la validación.
Si damos un vistazo a la jerarquía de clases del módulo edit
podemos ver que la clase que cumple con todo lo que necesitamos recibe el nombre de FormView
. FormView
está formada por el mixin TemplateResponseMixin
que ya conocemos y BaseFormView
que a su vez está formado por dos mixins más FormMixin
i ProcessFormView
.
FormMixin
nos proporciona los métodos para gestionar el form y ProcessFormView
que hijo de View nos proporciona los métodos de respuesta a las llamadas GET, PUT y POST. El equivalente del código anterior utilizando las generic class views nos queda como:
class ContactView(FormView): form_class=ContactForm success_url = reverse_lazy('main_thanks') template_name = 'main/index.html' def form_valid(self, form): # process data print form.cleaned_data return super(ContactView, self).form_valid(form)
es decir, hemos de decirle a la clase qué formulario utilizaremos, esto lo hacemos en el atributo form_class
o bien reescribiendo el método get_form_class
que nos ha proporcionado el mixin FormMixin
o instanciando el formulario directamente si sobrescribimos get_form
Los métodos form_valid
y form_invalid
nos permiten definir las acciones que realizaremos con el formulario. Normalmente nos bastará con sobrescribir o extender el método form_valid
pera adapta-lo a nuestras necesidades.
Entonces, nos podemos preguntar, ¿quién llama al método is_valid
del formulario? Si seguimos la jerarquía de clases veremos que lo hace el método post que es implementado por ProcessFormView
. Recordemos que un mixin tienen acceso a todo el objeto que lo utiliza, y por tanto también tiene acceso a los métodos del mixin FormMixin
.
Supongamos que queremos que nuestro formulario tome unos ciertos valores iniciales. Esto lo podemos conseguir utilizando el atributo initial, definido a FormMixin, o bien a partir de la rescritura del método get_inital. Según cómo sea nuestra aplicación nos convendrá utilizar un método u otro.
class ContactView(FormView): form_class=ContactForm success_url = reverse_lazy('main_thanks') template_name = 'main/index.html' initial = {'subject': u'This is a test form'} def form_valid(self, form): # process data print form.cleaned_data return super(ContactView, self).form_valid(form)
Si además tenemos que pasar datos adicionales a la plantilla Django que renderiza el formulario, FormMixin nos proporciona un viejo conocido, el método get_context_data, cuyo funcionamiento es idéntico al que tenía en TemplateView. Importante! Ahora tenemos que cuidar que el método get_context_data extienda el método padre, ya que en el parámetro kwargs nos viene la definición del formulario. Para evitar tener que adentrarnos en las complejidades de qué hace o deja de hacer el método, es mucho mejor llamar al método padre y a partir de aquí añadirle las variables que necesitemos:
class ContactView(FormView): form_class=ContactForm success_url = reverse_lazy('main_thanks') template_name = 'main/index.html' initial = {'subject': u'This is a test form'} def form_valid(self, form): # process data print form.cleaned_data return super(ContactView, self).form_valid(form) def get_context_data(self, **kwargs): context = super(ContactView, self).get_context_data(**kwargs) context['msg'] = u'Hello World' return context
Y no perdamos de vista que estamos trabajando utilizando clases y programación orientada a objetos, por lo que podemos ir añadiendo los métodos que necesitemos a la clase de manera que cada función por sí misma nos quede manejable.
Y ya para acabar el post un pequeño aviso, he utilizado reverse_lazy
pera obtener el valor de la url, pero esta funció no está en Django 1.3. Para utilizar el nombre de la url en lugar de la url en sí, en Django 1.3 hemos de sobreescribir get_success_url
o bien utilizar un snippet que podemos encontrar en Django Snippets que resuelve el problema de manera muy elegante
from django.utils.functional import lazy from django.core.urlresolvers import reverse reverse_lazy = lambda name=None, *args : lazy(reverse, str)(name, args=args)
En el próximo artículo veremos cómo podemos acceder y mostrar un objeto.