La mejor forma de construir un sitio que guste a la gente es atendiendo a sus comentarios. Muchos sitios parecen olvidar esto y ocultan los detalles de su contacto lo máximo posible.
Cuando tu sitio tiene millones de usuarios, esto puede ser una estrategia razonable. En cambio, cuando intentas formarte una audiencia, deberías pedir comentarios cada vez que se presente la oportunidad. Escribamos entonces un simple formulario para comentarios, y usémoslo para entender cómo funciona el framework de Django.
Comenzaremos agregando (r'^contact/$', 'mysite.books.views.contact')
al
URLconf, y luego definamos nuestro formulario. Los formularios en Django se
crean de una manera similar a los modelos: declarativamente, empleando una
clase de Python. He aquí la clase para nuestro simple formulario. Por
convención, lo insertaremos en un nuevo archivo forms.py
dentro del
directorio de nuestra aplicación:
from django import newforms as forms
TOPIC_CHOICES = (
('general', 'General enquiry'),
('bug', 'Bug report'),
('suggestion', 'Suggestion'),
)
class ContactForm(forms.Form):
topic = forms.ChoiceField(choices=TOPIC_CHOICES)
message = forms.CharField()
sender = forms.EmailField(required=False)
Un formulario de Django es una subclase de django.newforms.Form
,
tal como un modelo de Django es una subclase de
django.db.models.Model
. El módulo django.newforms
también
contiene cierta cantidad de clases Field
para los campos. Una
lista completa de éstas últimas se encuentra disponible en la
documentación de Django.
Nuestro ContactForm
consiste de tres campos: un asunto (topic
), que se
puede elegir entre tres opciones; un mensaje (message
), que es un campo de
caracteres; y un remitente (sender
), que es un campo de correo electrónico y
es opcional (porque incluso los comentarios anónimos pueden ser
útiles). Hay una cantidad de otros tipos de campos disponibles, y
puedes escribir nuevos tipos si ninguno cubre tus necesidades.
El objeto formulario sabe cómo hacer una cantidad de cosas útiles por
sí mismo. Puede validar una colección de datos, puede generar sus
propios "widgets" de HTML, puede construir un conjunto de mensajes
de error útiles. Y si no quieres diseñar el formulario a mano, puede
incluso renderizar el formulario entero. Incluyamos esto en una vista y
veámoslo en acción. En views.py
:
from django.db.models import Q
from django.shortcuts import render_to_response
from models import Book
from forms import ContactForm
def search(request):
query = request.GET.get('q', '')
if query:
qset = (
Q(title__icontains=query) |
Q(authors__first_name__icontains=query) |
Q(authors__last_name__icontains=query)
)
results = Book.objects.filter(qset).distinct()
else:
results = []
return render_to_response("books/search.html", {
"results": results,
"query": query
})
def contact(request):
form = ContactForm()
return render_to_response('contact.html', {'form': form})
y en contact.html
:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
<title>Contact us</title>
</head>
<body>
<h1>Contact us</h1>
<form action="." method="POST">
<table>
{{ form.as_table }}
</table>
<p><input type="submit" value="Submit"></p>
</form>
</body>
</html>
La línea más interesante aquí es {{ form.as_table }}
. form
es
nuestra instancia de ContactForm
, que fue pasada al
render_to_response
. as_table
es un método de ese objeto que
reproduce el formulario como una secuencia de renglones de una tabla
(también pueden usarse as_ul
y as_p
). El HTML generado se ve
así:
<tr>
<th><label for="id_topic">Topic:</label></th>
<td>
<select name="topic" id="id_topic">
<option value="general">General enquiry</option>
<option value="bug">Bug report</option>
<option value="suggestion">Suggestion</option>
</select>
</td>
</tr>
<tr>
<th><label for="id_message">Message:</label></th>
<td><input type="text" name="message" id="id_message" /></td>
</tr>
<tr>
<th><label for="id_sender">Sender:</label></th>
<td><input type="text" name="sender" id="id_sender" /></td>
</tr>
Observa que las etiquetas <table>
y <form>
no se han incluido;
debes definirlas por tu cuenta en la plantilla. Esto te da control
sobre el comportamiento del formulario. Los elementos label sí se
incluyen, y permiten mejorar la accesibilidad de los formularios.
Nuestro formulario actualmente utiliza un widget <input type="text">
para el campo del mensaje. Pero no queremos restringir a nuestros usuarios a una
sola línea de texto, así que la cambiaremos por un widget <textarea>
:
class ContactForm(forms.Form):
topic = forms.ChoiceField(choices=TOPIC_CHOICES)
message = forms.CharField(**widget=forms.Textarea()**)
sender = forms.EmailField(required=False)
El framework de formularios divide la lógica de presentación para cada campo, en un conjunto de widgets. Cada tipo de campo tiene un widget por defecto, pero puedes sobreescribirlo fácilmente, o proporcionar uno nuevo desarrollado por ti.
Por el momento, si se suministra el formulario, no sucede nada. Agreguemos nuestras reglas de validación:
def contact(request):
if request.method == 'POST':
form = ContactForm(request.POST)
else:
form = ContactForm()
return render_to_response('contact.html', {'form': form})
Una instancia de formulario puede estar en uno de dos estados: bound (vinculado) o unbound (no vinculado). Una instancia bound se construye con un diccionario (o un objeto que funcione como un diccionario) y sabe cómo validar y volver a representar sus datos. Un formulario unbound no tiene datos asociados y simplemente sabe cómo representarse a sí mismo.
Intenta hacer clic en Submit en el formulario vacío. La página se volverá a cargar, mostrando un error de validación que informa que nuestro campo de mensaje es obligatorio.
Intenta también ingresar una dirección de correo electrónico
inválida. El EmailField
sabe cómo validar estas direcciones, por
lo menos a un nivel razonable.