Django class based views (III)
Escrit per Aaloy a 20 de February , 2012 a les 9:09 p.m.
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': # 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,
})
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):
# process data
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):
# process data
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):
# 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
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!
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
Django class based views (II)
Escrit per Aaloy a 20 de February , 2012 a les 12:16 a.m.
En aquesta segona part veurem alguns dels usos més interessants de les class based views per a presentar informació i estalviar-nos feina.
TemplateView
Com ja vàrem veure a la primera part el TemplateView ens estalvia molta feina respecte a la manera tradicional de fer les coses, sols pel fet de passar ja el RequestContext ja recomanaria passar-nos al nou món de les Class Based Views, però encara hi ha més!
TemplateView incorpora dos mètodes més get_context_data i get.
Amb el get el que fa es cridar al mètode render_to_response que
ens proporciona el mixin TemplateResponseMixin i passant-li com a
arguments el diccionari que ens ha de tornar el mètode
get_context_data.
Per defecte get_context_data ens retorna un diccionari amb els
paràmetres que venen de la url, així si la nostra url és
url(r'^test3/(?P<id>\d+)/$', Test3View.as_view(),
name="test3-class-view"),
llavors get_context_data conté el diccionari {'id': 3} i a la
plantilla es passarà una variable anomenada params amb aquest
diccionari.
Suposem però que no és això exactament el que volem. Volem que passi
a la plantilla una variable anomenanda msg amb una salutació per als
lectors del blog.
El que farem serà sobreescriure get_context_data per a que passi la
informació que nosaltres volem a la plantilla.
class Test3View(TemplateView):
template_name = 'main/index.html'
def get_context_data(self, **kwargs):
context = super(Test3View, self).get_context_data(**kwargs)
context['id'] = self.kwargs.get('id')
context['msg'] = u'Hello blog!'
return context
Estrictament parlant no faria falta cridar al mètode pare de
get_context_data i de fet estam passant també la variable params
però fer-ho no fa mal i ens proporciona un cert grau de protecció
davant possibles problemes si un dia decidim que Test3View ja no
heretarà de TemplateView sinó d'una altra classe on ja s'hi posi
altra informació al contexte.
Al context_data i podem posar tota la informació que necessitem,
fixem-nos que és el mateix que feiem amb les funcions que acabaven
cridant al render_to_template sols que ara tot està més estructurat.
Suposem que volem que els missatge no estigui prefixat sinó que es generi a partir del resultat d'una funció. Abans hauríem creat la funció en algun lloc, a un mòdul apart, abans del mètode que renderitza la plantilla, ... Ara ho podem encapsular dins la mateixa classe
class Test3View(TemplateView):
template_name = 'main/index.html'
def get_message(self):
return u'Hello blog'
def get_context_data(self, **kwargs):
context = super(Test3View, self).get_context_data(**kwargs)
context = dict()
context['id'] = self.kwargs.get('id')
context['msg'] = self.get_message()
return context
I encara tenim més flexibilitat, gràcies al mixin
TemplateResponseMixin podem definir quina plantilla s'utilitzarà
segons ens convingui
Suposem que el paràmetre id ens serveix per a definir el nom de la plantilla, de manera que si existeix una plantilla que hi casi la presentarà i si no es quedarà amb la plantilla per defecte. Podríem fer
class Test3View(TemplateView):
template_name = 'main/index.html'
def get_message(self):
return u'Hello blog'
def get_template_names(self):
plantilla = 'main/index%s.html' % self.kwargs.get('id')
return [plantilla, ] + super(Test3View, self).get_template_names()
def get_context_data(self, **kwargs):
context = super(Test3View, self).get_context_data(**kwargs)
context['id'] = self.kwargs.get('id')
context['msg'] = self.get_message()
return context
Com veis la potència i la flexibilitat del que es pot fer amb les class based views no té res a veure amb el que es podia fer abans. Ara tenim molta més flexibilitat, més encapsulació i menys codi a escriure.
Al propert post parlarem de formularis...
Traducciones/Translations by apertium
0 comentaris, 0 trackbacks (URL) , Tags: Python Django
