Django class based views (III)
En aquest apunt veurem com fer servir les generic class views per a fer feina amb formularis.
Seguirem la documentació de Django que tracta dels formularis. Allà ens fa referència a un formulari de contacte creat com:
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)
i la vista associada
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
if form.is_valid():
return HttpResponseRedirect('/thanks/')
else:
form = ContactForm()
return render_to_response('contact.html', {'form': form, })
El que farem ara és fer el mateix però fent servir les generic class views.
El primer que hem de veure és què hem de fer servir. Necessitam gestionar un formulari, la seva validació i tornar la redirecció cap a una nova plana web en cas que tot vagi bé.
Si feim una ullada a la jerarquia de classes del mòdul edit
podem veure que la classes que compleix tot el que volem es diu FormView
.
FormView
està composada pel mixin TemplateResponseMixin
que ja coneixem i BaseFormView
que a la seva vegada està format per dos mixins més FormMixin
i ProcessFormView
.
FormMixin
ens proporciona els mètodes per a gestionar el form i ProcessFormView
que descendeix de View ens proporciona les respostes a les cridades get, post i put.
L'equivalent fent servir les generic class views ens queda:
class ContactView(FormView):
form_class = ContactForm
success_url = reverse_lazy('main_thanks')
template_name = 'main/index.html'
def form_valid(self, form):
print form.cleaned_data
return super(ContactView, self).form_valid(form)
és a dir, hem de dir li a la classe quin formulari farem servir, això ho feim a form_class
o bé amb la rescriptura del mètode get_form_class
que ens ha proporcionat el mixin FormMixin
o fin i tot instanciant el formulari directament si fem la rescriptura de get_form
Els mètodes form_valid
i form_invalid
ens permeten definir què em de fer amb el formulari. Normalment ens bastarà sobreescriure el mètode form_valid
per a adaptar-lo a les nostres necessitats.
I qui crida al mètode is_valid
del formulari? Doncs el post
que prové de ProcessFormView
. Recordem que un mixin té accés a tot l'objecte que el fa servir, i per tant també té accés als mètodes el mixin FormMixin
.
Suposem ara que volem que el nostre formulari agafi uns certs valors inicials, això ho podem fer amb l'atribut initial
definit a FormMixin
o bé a partir de la reescriptura del mètode get_initial
, segons la nostra aplicació ens convindrà un mètode o un altre.
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):
print form.cleaned_data
return super(ContactView, self).form_valid(form)
Si a més hem de passar dades addicionals a la plantilla Django, FormMixin
ens proporciona un altre vell conegut, el mètode get_context_data
que fa el mateix que feia al TemplateView. Això sí, ara hem de tenir cura de cridar al mètode pare, ja que als kwargs hi ha la definició del formulari. Per a evitar-nos conèixer què fa i que no fa, millor cridar al mètode pare i a partir d'aquí afegir-hi les variables que necessitem:
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):
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
I no hem de perdre de vista que estam fent feina amb classes. Podem anar afegint els mètodes que necessitem a la classe, de manera que cada funció ens quedi manejable.
I ja per acabar un petit avís, he fet servir reverse_lazy
per a obtenir el valor de la url, però aquesta funció no hi és a Django 1.3. Per utilitzar el nom de la url enlloc de la url en sí, en Django 1.3 o bé hem de sobreescriure get_success_url
o bé utilitzar l'snippet que apareix a Django Snippets i que resol això d'una manera molt elegant:
from django.utils.functional import lazy
from django.core.urlresolvers import reverse
reverse_lazy = lambda name=None, *args: lazy(reverse, str)(name, args=args)
Això encara dóna per més, fins al proper article!