Diferencia entre revisiones de «Curso Python DGA 2011/django/forms»
(Página creada con '== Formularios== ===Primer formulario=== Vamos a actualizar la plantilla del detalle ('''encuesta/detalle.html''') para que tenga una etiqueta <nowiki><form></nowiki> <source la…') |
|||
Línea 93: | Línea 93: | ||
</source> | </source> | ||
− | + | ===Vistas genéricas: ¡todavía menos código!!=== | |
Hay tareas muy repetitivas en el desarrollo web: obtener datos de la base de datos, según unos parámetros de una URL, cargar una plantilla y devolver la plantilla procesada. Django proporciona un atajo llamado sistema de '''generic views'''. | Hay tareas muy repetitivas en el desarrollo web: obtener datos de la base de datos, según unos parámetros de una URL, cargar una plantilla y devolver la plantilla procesada. Django proporciona un atajo llamado sistema de '''generic views'''. | ||
Para convertir nuestra aplicación al uso de vistas genéricas hay que: | Para convertir nuestra aplicación al uso de vistas genéricas hay que: | ||
Línea 102: | Línea 102: | ||
En un proyecto decidiremos antes si es conveniente el uso de vistas genéricas (mejor que refactorizar) | En un proyecto decidiremos antes si es conveniente el uso de vistas genéricas (mejor que refactorizar) | ||
− | + | ====Modificar encuestas/urls.py URLconf ==== | |
<source lang="python" line="GESHI_NORMAL_LINE_NUMBERS"> | <source lang="python" line="GESHI_NORMAL_LINE_NUMBERS"> | ||
from django.conf.urls.defaults import * | from django.conf.urls.defaults import * |
Última revisión de 01:49 25 ago 2011
Contenido
Formularios
Primer formulario
Vamos a actualizar la plantilla del detalle (encuesta/detalle.html) para que tenga una etiqueta <form>
<h1>{{ encuesta.pregunta }}</h1> {% if mensaje_error %}<p><strong>{{ mensaje_error }}</strong></p>{% endif %} <form action="/encuestas/{{ encuesta.id }}/votar/" method="post"> {% csrf_token %} {% for opcion in encuesta.opcion_set.all %} <input type="radio" name="opcion" id="opcion{{ forloop.counter }}" value="{{ opcion.id }}" /> <label for="opcion{{ forloop.counter }}">{{ opcion.opcion }}</label><br /> {% endfor %} <input type="submit" value="Votar" /> </form>
Mirando el código:
- Se usa un radio button para cada opción de la encuesta. El valor de cada opción está asociado al de su id. El nombre de los radio es opcion, así cuando un usuario selecciona una opción y pulsa el botón de votar, envia por POST la información opcion=3 (con el número de id elegido)
- La acción del formulario es /encuestas/Plantilla:Encuesta.id/votar. Los datos se envían por POST. Muy importante porque enviar datos al servidor puede alterar el estado (a diferencia de GET)
- el forloop.counter indica cuántas veces ha entrado en el bucle.
- Se usa {% csrf_token %} para evitar Cross Site Request Forgeries. Importante usarlo siempre con POST. Para permitir el trabajo de csrf hay que añadir un método a la vista detalle.
from django.template import RequestContext
# ...
def detalle(request, encuesta_id):
enc = get_object_or_404(Encuesta, pk=encuesta_id)
return render_to_response('encuestas/detalle.html',
{'encuesta': enc},
context_instance=RequestContext(request))
Acción del formulario
Según la entrada de URLconf, cuando ejecutamos la acción de votar, se llama a la función votar, que gestiona los datos enviados y hace algo con ellos:
(r'^(?P<encuesta_id>\d+)/votar/$', 'votar')
La acción puede quedar así:
from django.shortcuts import render_to_response, get_object_or_404
from django.http import HttpResponseRedirect, HttpResponse
from django.core.urlresolvers import reverse
from django.template import RequestContext
from encuestas.models import Encuesta, Opcion
# ...
def votar(request, encuesta_id):
enc = get_object_or_404(Encuesta, pk=encuesta_id)
try:
opcion_seleccionada = enc.opcion_set.get(pk=request.POST['opcion'])
except (KeyError, Opcion.DoesNotExist):
# Vuelve a mostrar el formulario de votación
return render_to_response('encuestas/detalle.html', {
'encuesta': enc,
'mensaje_error': "No has seleccionado ninguna opción.",
}, context_instance=RequestContext(request))
else:
opcion_seleccionada.votos += 1
opcion_seleccionada.save()
# Siempre devuelve un HttpResponseRedirect después de tratar con éxito
# un formulario POST. Así se evita enviar dos veces la información cuando
# el usuario pulsa el botón retroceder.
return HttpResponseRedirect(reverse('encuestas.views.resultado', args=(enc.id,)))
return HttpResponse(u"Estás votando la encuesta %s." % encuesta_id)
Algunas cosas que todavía no hemos visto:
- request.POST es un objeto tipo diccionario que permite acceder a los datos enviados por su clave (request.POST['opcion'] devuelve el ID de la opción seleccionada como una cadena) Los valores de request.POST son siempre cadenas.
- Django también proporciona request.GET para acceder a los datos pasados por GET.
- request.POST['opcion'] lanza una excepción KeyError si no se envía esa información en POST. Por eso se trata la excepción.
- Después de incrementar el contador de la opción, el código devuelve un HttpResponseRedirect en lugar de un HttpResponse como hace normalmente.
- Se usa la función reverse() para evitar escribir a mano la URL en la vista. Parámetros: vista y argumentos.
La vista resultado puede ser así:
def resultado(request, encuesta_id):
enc = get_object_or_404(Encuesta, pk=encuesta_id)
return render_to_response('encuestas/resultado.html', {'encuesta': enc})
Y la plantilla resultado.html:
<h1>{{ encuesta.pregunta}}</h1> <ul> {% for opcion in encuesta.opcion_set.all %} <li>{{ opcion.opcion }} -- {{ opcion.votos }} voto{{ opcion.votos|pluralize }}</li> {% endfor %} </ul> <a href="/encuestas/{{ encuesta.id }}/">¿Votar otra vez?</a>
Vistas genéricas: ¡todavía menos código!!
Hay tareas muy repetitivas en el desarrollo web: obtener datos de la base de datos, según unos parámetros de una URL, cargar una plantilla y devolver la plantilla procesada. Django proporciona un atajo llamado sistema de generic views. Para convertir nuestra aplicación al uso de vistas genéricas hay que:
- Convertir el URLconf.
- Eliminar algunas vistas innecesarias.
- Corregir la gestión de las URLs por la nuevas vistas
En un proyecto decidiremos antes si es conveniente el uso de vistas genéricas (mejor que refactorizar)
Modificar encuestas/urls.py URLconf
from django.conf.urls.defaults import *
from django.views.generic import DetailView, ListView
from encuestas.models import Encuesta
urlpatterns = patterns('',
(r'^$',
ListView.as_view(
queryset=Encuesta.objects.order_by('-fecha_pub')[:5],
context_object_name='listado_ultimas',
template_name='encuestas/index.html')),
(r'^(?P<pk>\d+)/$',
DetailView.as_view(
model=Encuesta,
template_name='encuestas/detalle.html')),
url(r'^(?P<pk>\d+)/resultado/$',
DetailView.as_view(
model=Encuesta,
template_name='encuestas/resultado.html'),
name='resultado_encuesta'),
(r'^(?P<encuesta_id>\d+)/votar/$', 'encuestas.views.votar'),
)
Vamos a usar dos vistas genéricas: ListView y DetailView. Cada vista tiene que conocer:
- el modelo sobre el que actúa (model)
- DetailView necesita la clave primaria, llamada pk.(hay que cambiar encuesta_id a pk)
- El nombre resultado_encuesta de la vista resultado sirve para referirse a esta URL después.
- Por defecto la plantilla de DetailView se llama <nombre aplic.>/<nombre modelo>_detail.html. en nuestro caso será: encuestas/encuesta_detail.html Se puede usar el argumento template_name
- La plantilla de ListView se llama por defecto <nombre_modelo>_list.html
- El contexto lo gestiona de forma autmática generando encuesta_list. La copción context_object_name permite especificar ultimas_encuestas.
- Podemos eliminar ya las vistas creadas: index, detalle, resultado.
- Sobre la redicrección:
return HttpResponseRedirect(reverse('resultado_encuesta', args=(enc.id,)))