xpanel, lxde, linux, fbpanel opensuse, linux, instalacion gtk3, gnome, ambienti grafici PS3, mandos, Play Station, Linux edubuntu, ubuntu, linux fedora, linux, discapacitados visuales fuduntu, fedora, ubuntu, linux, distribuciones inkscape, grafica, linux, editor tux, tuz, iconos, mascota, linux artistx, distro, linux, artistas, graficos

Guía de Django, el web framework para perfeccionistas (6a parte).

Formularios.

Los formularios HTML son la columna vertebral de sitios Web interactivos. Este capítulo comprende cómo se puede utilizar Django para acceder a los datos de formulario emitidos por el usuario, validarlos, y hacer algo con ellos. En el camino, cubriremos los objetos HttpRequest y Form.

 

Obtener datos del objeto Request.

Los objetos HttpRequest, tienen una serie de atributos y de métodos que son interesantes y debería familiarizarse con ellos para saber lo que es posible hacer con ellos.

djangodesktop-1600x1200

Puede usar estos atributos para obtener información acerca de la petición actual (por ejemplo el usuario / navegador web que está cargando la página actual en su sitio Django) en el momento en que se ejecuta la la función de la vista.

 

Información sobre la URL.

Los objetos HttpRequest contienen varias piezas de información sobre la URL solicitada actualmente:

 

Usar siempre los atributos / métodos en lugar de codificar las direcciones URL en sus vista. Esto hace que el código sea más flexible y que pueda ser reutilizado en otros lugares. He aquí un simple ejemplo:

# BAD!
def current_url_view_bad(request):

return HttpResponse(“Welcome to the page at /current/”)

# GOOD
def current_url_view_good(request):

return HttpResponse(“Welcome to the page at %s” % request.path)

Otra información sobre la petición.

request.META es un diccionario Python que contiene todas las cabeceras HTTP disponibles para la solicitud dada, incluyendo la dirección IP del usuario y el agente de usuario (generalmente el nombre y la versión del navegador Web).  Las siguientes son algunas claves comunes en este diccionario:

 

  • HTTP_REFERER: La URL de referencia, si la hubiese.
  • HTTP_USER_AGENT: La cadena de agente de usuario (si existe) del navegador del usuario. Esto se parecerá a algo como lo siguiente: “Mozilla 5.0 (X11; U; Linux i686) Gecko/20080829 Firefox/2.0.0.17″
  • REMOTE_ADDR: La dirección IP del cliente.

Usted debe usar una cláusula try / except, o un método get() para manejar el caso de claves indefinidas, como en este ejemplo:

# BAD!
def ua_display_bad(request):

ua = request.META['HTTP_USER_AGENT'] # Might raise KeyError!
return HttpResponse(“Your browser is %s” % ua)

# GOOD (VERSION 1)
def ua_display_good1(request):

try:

ua = request.META['HTTP_USER_AGENT']

except KeyError:

ua = ‘unknown’

return HttpResponse(“Your browser is %s” % ua)

# GOOD (VERSION 2)
def ua_display_good2(request):

ua = request.META.get(‘HTTP_USER_AGENT’, ‘unknown’)
return HttpResponse(“Your browser is %s” % ua)

Le animamos a escribir una pequeña vista que muestra todos los datos de request.META para que pueda conocer lo que está disponible. Se podría parecer a esto:

def display_meta(request):

values = request.META.items()
values.sort()
html = []
for k, v in values:

html.append(‘<tr><td>%s</td><td>%s</td></tr>’ % (k, v))

return HttpResponse(‘<table>%s</table>’ % ‘n’.join(html))

django_slide_4

Información sobre datos emitidos.

Más allá de los metadatos básicos acerca de la solicitud, los objetos HttpRequest tienen dos atributos que contienen la información que envía el usuario: request.GET y request.POST. Ambas son objetos como diccionarios que le dan acceso a los datos GET y POST.

 

Los datos POST generalmente son emitidos desde un formulario HTML, mientras que los datos GET pueden provenir de un formulario o del querystring de la URL de la página.

 

Un ejemplo simple de gestión de formulario.

Creemos una vista simple que permita a los usuarios buscar en nuestra base de datos de libros por el título.

 

En general, hay dos partes para el desarrollo de un formulario: la interfaz de usuario HTML y el código de la vista que procesa los datos presentados. La primera parte es fácil, vamos a crear una vista que muestre un formulario de búsqueda:

from django.shortcuts import render_to_response

def search_form(request):

return render_to_response(‘search_form.html’)

Esta vista puede residir en cualquier parte de su ruta de acceso Python. Para este ejemplo, colocarla en books/views.py.

La plantilla de acompañamiento, search_form.html, podría tener este aspecto:

<html>
<head>

<title>Search</title>

</head>
<body>

<form action=”/search/” method=”get”>

<input type=”text” name=”q”>
<input type=”submit” value=”Search”>

</form>

</body>
</html>

El patrón URL en urls.py podría ser algo como esto:

urlpatterns = patterns(”,

# …
(r’^search-form/$’, views.search_form),
# …

)

django-body1

Ahora, si ejecuta el runserver y visita http://127.0.0.1:8000/search-form/, verá la interfaz de búsqueda. Bastante simple.

Intente emitir el formulario, sin embargo, y obtendrá un error 404 de Django. El formulario apunta a la URL /search/, que aún no ha sido implementada. Vamos a arreglar eso con una segunda función de vista:

# urls.py
urlpatterns = patterns(”,

# …
(r’^search-form/$’, views.search_form),
(r’^search/$’, views.search),
# …

)

# views.py
def search(request):

if ‘q’ in request.GET:

message = ‘You searched for: %r’ % request.GET['q']

else:

message = ‘You submitted an empty form.’

return HttpResponse(message)

Por el momento, esto sólo muestra el término de búsqueda del usuario de modo que pueda asegurarse que los datos se presentan a Django correctamente y para que pueda tener una idea de cómo los términos de la búsqueda fluyen a través del sistema. En resumen, esto es lo que sucede:

 

  1. El formulario HTML define una variable q. Cuando se emite, el valor de q se envía a través de GET (method = “get”) a la URL /search/.
  2. La vista Django que se encarga de la dirección /search/ tiene acceso al valor de q en request.GET.

Tenga en cuenta que comprobamos explícitamente que ‘q’ existe en request.GET. Como hemos señalado, usted no debe confiar en nada presentado por los usuarios. Si no ha añadido esta verificación, cualquier emisión de un formulario vacío lanzaría KeyError en la vista:

# BAD!
def bad_search(request):

# The following line will raise KeyError if ‘q’ hasn’t
# been submitted!
message = ‘You searched for: %r’ % request.GET['q']
return HttpResponse(message)

Los datos POST funcionan de la misma manera que los datos GET. ¿Cuál es la diferencia entre GET y POST?  Utilice GET cuando el acto de emitir el formulario es sólo una solicitud para “obtener” datos. Utilice POST siempre que el acto de emitir el formulario tenga algunos efectos secundarios de actualización de datos o enviar un e-mail, o algo más de la simple exhibición de los datos.  En nuestro ejemplo de búsqueda de libro, estamos utilizando GET porque la consulta no cambia ningún dato en nuestro servidor. Ahora que hemos verificado que request.GET se está pasando correctamente, vamos a conectar la consulta de búsqueda del usuario con nuestra base de datos de libros (de nuevo, en views.py):

from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book

def search(request):

if ‘q’ in request.GET and request.GET['q']:

q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response(‘search_results.html’, {‘books’: books, ‘query’: q})

else:

return HttpResponse(‘Please submit a search term.’)

El código de la plantilla search_results.html podría ser algo como esto:

<p>You searched for: <strong>{{ query }}</strong></p>

{% if books %}

<p>Found {{ books|length }} book{{ books|pluralize }}.</p>
<ul>

{% for book in books %}
<li>{{ book.title }}</li>
{% endfor %}

</ul>

{% else %}

<p>No books matched your search criteria.</p>

{% endif %}

django_templates

Mejorar nuestro ejemplo de gestión de formulario simple.

En primer lugar, nuestra gestión de la vista search() de una consulta vacía es pobre – estamos mostrando solo un mensaje “Por favor, envíe un término de búsqueda”, que requiere que el usuario pulse el botón Atrás del navegador. Esto es horrible y poco profesional.

 

Sería mucho mejor volver a mostrar el formulario, con un error sobre él, de modo que el usuario pueda volver a intentarlo inmediatamente. La forma más sencilla de hacerlo sería la de renderizar la plantilla de nuevo, de esta forma:

from django.http import HttpResponse
from django.shortcuts import render_to_response
from mysite.books.models import Book

def search_form(request):

return render_to_response(‘search_form.html’)

def search(request):

if ‘q’ in request.GET and request.GET['q']:

q = request.GET['q']
books = Book.objects.filter(title__icontains=q)
return render_to_response(‘search_results.html’, {‘books’: books, ‘query’: q})

else:

return render_to_response(‘search_form.html’, {‘error’: True})

Hemos mejorado search() renderizando la plantilla search_form.html de nuevo, si la consulta está vacía. Y ya que tenemos que mostrar un mensaje de error en esa plantilla, pasamos una variable de plantilla. Ahora podemos editar search_form.html para comprobar la variable de error:

<html>
<head>

<title>Search</title>

</head>
<body>

{% if error %}
<p style=”color: red;”>Please submit a search term.</p>
{% endif %}
<form action=”/search/” method=”get”>

<input type=”text” name=”q”>
<input type=”submit” value=”Search”>

</form>

</body>
</html>

Todavía podemos utilizar esta plantilla desde nuestra vista original, search_form(), ya que search_form() no pasa el error a la plantilla – el mensaje de error no se mostrará en ese caso.

 

Con este cambio es una aplicación mejor, pero ahora surge la pregunta: ¿es una vista search_form() dedicada realmente necesaria? Tal como está, una solicitud a la dirección URL /search/ (sin los parámetros GET) mostrará el formulario vacío (pero con un error). Podemos eliminar la vista search_form(), junto con su patrón URL asociado, siempre y cuando cambiemos search() para ocultar el mensaje de error cuando alguien visite /search/ sin parámetros GET:

def search(request):

error = False
if ‘q’ in request.GET:

q = request.GET['q']
if not q:

error = True

else:

books = Book.objects.filter(title__icontains=q)
return render_to_response(‘search_results.html’, {‘books’: books, ‘query’: q})

return render_to_response(‘search_form.html’, {‘error’: error})

django-templates-0-317x450

En esta vista actualizada, si un usuario visita /search/ sin parámetros GET, verá el formulario de búsqueda sin mensaje de error. Si un usuario envía el formulario con un valor vacío para ‘q’, verá el formulario de búsqueda con un mensaje de error. Y, por último, si un usuario envía el formulario con un valor no vacío de ‘q’, verá los resultados de búsqueda.

 

Podemos hacer una mejora final a esta aplicación, para quitar un poco de redundancia. Ahora que hemos mezclado las dos vistas y URLs en una sola y /search/ maneja tanto la pantalla del formulario de búsqueda como la del resultado, el formulario HTML en search_form.html no tiene que codificar una URL a pelo. En lugar de esto:

puede cambiarse a:

El action = “” significa “Enviar el formulario a la misma URL que la página actual.” Con este cambio, usted no tendrá que acordarse de cambiar la acción, incluso si alguna vez la vista search() apunta a otra URL.

 

Validación simple.

Nuestro ejemplo de búsqueda es todavía bastante simple, especialmente en términos de validación de sus datos, hacemos una mera comprobación para asegurar que la consulta de búsqueda no está vacía. Muchos de los formularios HTML incluyen un nivel de validación que es más complejo que asegurar que el valor no está vacío. Todos hemos visto la siguientes mensajes de error en los sitios Web:
“Por favor introduzca una dirección de correo electrónico”.
“Por favor, introduzca un código postal de cinco dígitos válido EE.UU.”
“Por favor, introduzca una fecha válida con formato AAAA-MM-DD”.
“Por favor, introduzca una contraseña que sea por lo menos de 8 caracteres de longitud y contenga al menos un número”.

Vamos a afinar la vista search() para validar que el término de búsqueda sea menor o igual a 20 caracteres de largo. ¿Cómo podemos hacerlo? Lo más sencillo sería integrar la lógica directamente en la vista, así:

def search(request):

error = False
if ‘q’ in request.GET:

q = request.GET['q']
if not q:

error = True

elif len(q) > 20:

error = True

else:

books = Book.objects.filter(title__icontains=q)
return render_to_response(‘search_results.html’,{‘books’: books, ‘query’: q})

return render_to_response(‘search_form.html’, {‘error’: error})

Ahora bien, si se intenta emitir una consulta de búsqueda mayor de 20 caracteres de largo, obtendrá un mensaje de error. Pero ese mensaje de error en search_form.html actualmente dice: “Por favor, introduzca un término de búsqueda.”, así que tendremos que cambiarlo para ser exactos en ambos casos (una búsqueda vacía o un término de búsqueda demasiado largo).

<html>
<head>

<title>Search</title>
</head>
<body>

{% if error %}
<p style=”color: red;”>

Please submit a search term
20 characters or shorter.

</p>
{% endif %}
<form action=”/search/” method=”get”>

<input type=”text” name=”q”>
<input type=”submit” value=”Search”>

</form>

</body>
</html>

django4

Hay algo mal en esto. Nuestro único mensaje de error es potencialmente confuso. ¿Por qué el mensaje de error para un valor vacío menciona nada sobre un límite de 20 caracteres? Los mensajes de error deben ser específicos y claros.

El problema es que estamos utilizando un solo valor boolean para el error, cuando habría que utilizar una lista de cadenas de mensajes de error. He aquí cómo podemos solucionarlo:

def search(request):

errors = []
if ‘q’ in request.GET:

q = request.GET['q']
if not q:

errors.append(‘Enter a search term.’)

elif len(q) > 20:

errors.append(‘Please enter at most 20 characters.’)

else:

books = Book.objects.filter(title__icontains=q)
return render_to_response(‘search_results.html’, {‘books’: books, ‘query’: q})

return render_to_response(‘search_form.html’, {‘errors’: errors})

Entonces tenemos que hacer un pequeño ajuste en la plantilla search_form.html para reflejar que se pasó una lista de errores, en lugar de un error boolean:

<html>
<head>

<title>Search</title>
</head>
<body>

{% if errors %}
<ul>

{% for error in errors %}

<li>{{ error }}</li>

{% endfor %}

</ul>
{% endif %}
<form action=”/search/” method=”get”>

<input type=”text” name=”q”>
<input type=”submit” value=”Search”>

</form>

</body>
</html>

Hacer un formulario de contacto.

Aunque hemos reiterado en el ejemplo del formulario de búsqueda de libros varias veces y lo hemos mejorado, sigue siendo muy sencillo: basta con un solo campo, ‘q’. Al ser tan simple, ni siquiera usamos la librería de formularios de Django para tratar con él. Pero las formas más complejas requieren tratamientos más complejos, y ahora vamos a desarrollar algo más complejo: un formulario de contacto de la web que permite a los usuarios del sitio envíar comentarios, junto con un e-mail de retorno. Después que el formulario es emitido y los datos son validados, automáticamente le enviaremos un mensaje por correo electrónico al personal del sitio.

Empezaremos con nuestra plantilla, contact_form.html:

<html>
<head>

<title>Contact us</title>

</head>
<body>

<h1>Contact us</h1>
{% if errors %}
<ul>

{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}

</ul>
{% endif %}

<form action=”/contact/” method=”post”>

<p>Subject: <input type=”text” name=”subject”></p>
<p>Your e-mail (optional): <input type=”text” name=”e-mail”></p>
<p>Message: <textarea name=”message” rows=”10″ cols=”50″></textarea></p>
<input type=”submit” value=”Submit”>

</form>

</body>
</html>

Hemos definido tres campos: el asunto, la dirección de correo electrónico y el mensaje. El segundo es opcional, pero los otros dos campos son obligatorios. Tenga en cuenta que estamos usando method = “post” aquí en lugar de method = “get” ya que este formulario de emisión tiene un efecto secundario – que envía un e-mail. Si seguimos el camino establecido por nuestra vista search() de la sección anterior, una versión de nuestra vista contact() podría tener este aspecto:

from django.core.mail import send_mail
from django.http import HttpResponseRedirect
from django.shortcuts import render_to_response

def contact(request):

errors = []
if request.method == ‘POST’:

if not request.POST.get(‘subject’, ”):

errors.append(‘Enter a subject.’)

if not request.POST.get(‘message’, ”):

errors.append(‘Enter a message.’)

if request.POST.get(‘e-mail’) and ‘@’ not in request.POST['e-mail']:

errors.append(‘Enter a valid e-mail address.’)

if not errors:

send_mail(request.POST['subject'], request.POST['message'],
request.POST.get(‘e-mail’, ‘noreply@example.com’),
['siteowner@example.com'],

)
return HttpResponseRedirect(‘/contact/thanks/’)

return render_to_response(‘contact_form.html’,{‘errors’: errors})

DjangoArchitecture-JeffCroft

Varias cosas nuevas están sucediendo aquí:

  • Comprobamos que request.method es ‘POST’. Esto será cierto sólo en el caso de una emisión del formulario, no será cierto si alguien está solamente viendo el formulario de contacto. Esto hace que sea una buena forma de aislar el caso de “pantalla del formulario” del caso de”transformación del formulario”.
  • En lugar de request.GET, estamos usando request.POST para acceder a los datos del formulario emitido. Esto es necesario porque el código de contact_form.html usa method = “post”.
  • Contamos con dos campos obligatorios, asunto y mensaje, así que tenemos que validar ambos. Notar que estamos utilizando request.POST.get() y proporcionando una cadena en blanco como el valor por defecto.
  • Aunque el campo de correo electrónico no es obligatorio, debemos aún validarlo si es emitido. Nuestro algoritmo de validación aquí es frágil – estamos comprobando que la cadena contiene un carácter @. En el mundo real, necesitaríamos una validación más robusta.
  • Estamos usando la función django.core.mail.send_mail para enviar un e-mail. Esta función tiene cuatro argumentos necesarios: el asunto del e-mail, el cuerpo del correo electrónico, la dirección del emisor, y una lista de direcciones de los destinatarios. send_mail está contenida en la clase de Django E-mailMessage, que proporciona características avanzadas tales como archivos adjuntos, emails multiplart, y el control total de los encabezados del correo electrónico.
  • Después de enviar el e-mail, redirigimos a una página de “éxito” devolviendo un objeto HttpResponseRedirect. Usted siempre debe enviar una redirección para el éxito de las peticiones POST. Es una de las mejores prácticas del desarrollo web.

Esto vista funciona, pero las funciones de validación son enrevesadas. Imagine la tramitación de un formulario con una docena de campos, ¿de verdad quieres tener que escribir todas esas sentencias if?

 

Otro problema es volver a mostrar el formulario. En el caso de errores de validación, es mejor práctica volver a mostrar el formulario con los datos presentados anteriormente ya rellenados para que el usuario puede ver lo que hizo mal (y no tener que volver a introducir los datos en los campos que ha emitido correctamente). Nosotros manualmente podríamos pasar los datos POST de nuevo a la plantilla, pero habría que editar cada campo HTML para insertar el valor adecuado en el lugar adecuado:

# views.py
def contact(request):

errors = []
if request.method == ‘POST’:

if not request.POST.get(‘subject’, ”):

errors.append(‘Enter a subject.’)

if not request.POST.get(‘message’, ”):

errors.append(‘Enter a message.’)

if request.POST.get(‘e-mail’) and ‘@’ not in request.POST['e-mail']:

errors.append(‘Enter a valid e-mail address.’)

if not errors:

send_mail(
request.POST['subject'],
request.POST['message'],
request.POST.get(‘e-mail’, ‘noreply@example.com’),
['siteowner@example.com'],
)

return HttpResponseRedirect(‘/contact/thanks/’)

return render_to_response(‘contact_form.html’, {‘errors’: errors,’subject’: request.POST.get(‘subject’, ”),

‘message’: request.POST.get(‘message’, ”),’e-mail’: request.POST.get(‘e-mail’, ”),

})

# contact_form.html
<html>
<head>

<title>Contact us</title>

</head>
<body>

<h1>Contact us</h1>
{% if errors %}
<ul>

{% for error in errors %}
<li>{{ error }}</li>
{% endfor %}

</ul>
{% endif %}
<form action=”/contact/” method=”post”>

<p>Subject: <input type=”text” name=”subject” value=”{{ subject }}”></p>
<p>Your e-mail (optional):

<input type=”text” name=”e-mail” value=”{{ e-mail }}”>

</p>
<p>Message:

<textarea name=”message” rows=”10″ cols=”50″>
**{{ message }}**
</textarea>

</p>
<input type=”submit” value=”Submit”>

</form>

</body>
</html>

Se trata de una gran cantidad de código, e introduce un montón de posibilidades de error humano. Veremos alguna librería de alto nivel que gestione las tareas relacionadas con los formularios y la validación.

mvc-model

Su primera clase de formulario.

Django viene con una librería de formularios, llamada django.forms, que se encarga de muchas de las cuestiones que hemos estado viendo en este capítulo – desde la muestra de formularios HTML a la validación. Volvamos a hacer nuestro formulario de contacto utilizando el marco de formularios de Django.

 

La principal manera de utilizar el marco de formularios es definir una clase Form por cada
HTML. En nuestro caso, sólo tenemos una
, así que tendremos una clase Form. Esta clase puede residir en cualquier lugar, incluyendo directamente en el archivo views.py, pero la convención es mantener las clases Form en un archivo llamado forms.py. Cree este archivo en el mismo directorio que el views.py, y escriba lo siguiente:

from django import forms

class ContactForm(forms.Form):

subject = forms.CharField()
e-mail = forms.EmailField(required=False)
message = forms.CharField()

Esto es bastante intuitivo, y es similar a la sintaxis de modelo de Django. Cada campo en el formulario está representado por un tipo de clase Field – Charfield y EmailField son los únicos tipos Field utilizados aquí – como atributos de una clase Form. Cada campo es obligatorio por defecto, así que para hacer e-mail opcional, especificamos required = False.

Vayamos al intérprete interactivo de Python y veamos lo que esta clase puede hacer. Lo primero que puede hacer es mostrarse como HTML:

<<< from contact.forms import ContactForm
<<< f = ContactForm()
<<< print f
<tr><th><label for=”id_subject”>Subject:</label></th><td>
<input type=”text” name=”subject” id=”id_subject” /></td></tr>
<tr><th><label for=”id_e-mail”>E-mail:</label></th><td>
<input type=”text” name=”e-mail” id=”id_e-mail” /></td></tr>
<tr><th><label for=”id_message”>Message:</label></th><td>
<input type=”text” name=”message” id=”id_message” /></td></tr>

Django añade una etiqueta a cada campo, junto con las etiquetas para accesibilidad. La idea es hacer que el comportamiento por defecto sea tan satisfactorio como sea posible.

Esta es la salida por defecto en el formato de un <table> HTML, pero hay otras salidas preconstruidas:

<<< print f.as_ul()
<li><label for=”id_subject”>Subject:</label>
<input type=”text” name=”subject” id=”id_subject” /></li>
<li><label for=”id_e-mail”>E-mail:</label>
<input type=”text” name=”e-mail” id=”id_e-mail” /></li>
<li><label for=”id_message”>Message:</label>
<input type=”text” name=”message” id=”id_message” /></li>

<<< print f.as_p()
&ltp>&ltlabel for=”id_subject”>Subject:&lt/label>
&ltinput type=”text” name=”subject” id=”id_subject” />&lt/p>
&ltp>&ltlabel for=”id_e-mail”>E-mail:&lt/label>
&ltinput type=”text” name=”e-mail” id=”id_e-mail” />&lt/p>
&ltp>&ltlabel for=”id_message”>Message:&lt/label>
&ltinput type=”text” name=”message” id=”id_message” />&lt/p>

Tenga en cuenta que la etiquetas de apertura y cierre <table>, <ul>, y <form> no son incluidas en la salida, de forma que usted puede agregar las filas adicionales si es necesario.

Estos métodos son métodos abreviados para el caso común de “mostrar todo el formulario.” Usted también puede mostrar el código HTML de un campo particular:

<<< print f['subject']
<input type=”text” name=”subject” id=”id_subject” />
<<< print f['message']
<input type=”text” name=”message” id=”id_message” />

La segunda cosa que podemos hacer con los objetos Form es validar los datos. Para ello, crear un nuevo objeto Form y pasarle un diccionario de datos que asigne los nombres de campo a los datos:

<<< f = ContactForm({‘subject’: ‘Hello’, ‘e-mail’: ‘adrian@example.com’,
… ‘message’: ‘Nice site!’})

Una vez ha asociado los datos con una instancia Form, ha creado un Form bound (ligado):

<<< f.is_bound
True

Llame al método is_valid() de cualquier Form ligado para averiguar si sus datos son válidos. Hemos pasado un valor válido para cada campo, por lo que el formulario en su totalidad es válido:

<<< f.is_valid()
True

Si no se pasa el campo de correo electrónico, sigue siendo válido, porque hemos especificado required = False para ese campo:

<<< f = ContactForm({‘subject’: ‘Hello’, ‘message’: ‘Nice site!’})
<<< f.is_valid()
True

Pero si dejamos fuera el asunto o el mensaje, el formulario ya no es válido:

<<< f = ContactForm({‘subject’: ‘Hello’})
<<< f.is_valid()
False
<<< f = ContactForm({‘subject’: ‘Hello’, ‘message’: ”})
<<< f.is_valid()
False

Usted puede ver los detalles obteniendo mensajes de error específicos por campo:

<<< f = ContactForm({‘subject’: ‘Hello’, ‘message’: ”})
<<< f['message'].errors
[u'This field is required.']
<<< f['subject'].errors
[]
<<< f['e-mail'].errors
[]

Cada instancia Form ligada tiene un atributo errors que proporciona un diccionario que asigna nombres de campos a listas de mensajes de error:

<<< f = ContactForm({‘subject’: ‘Hello’, ‘message’: ”})
<<< f.errors
{‘message’: [u'This field is required.']}

Por último, para los casos de instancias de formulario cuyos datos se ha encontrado ser válidos, está disponible el atributo cleaned_data. Este es un diccionario de los datos emitidos, “limpiado”. El marco de formularios de Django, no sólo valida los datos, sino que los limpia para convertir de valores a los tipos de Python adecuados, como se muestra aquí:

<<< f = ContactForm({‘subject’: ‘Hello’, ‘e-mail’: ‘adrian@example.com’,
… ‘message’: ‘Nice site!’})
<<< f.is_valid()
True
<<< f.cleaned_data
{‘message’: u’Nice site!’, ‘e-mail’: u’adrian@example.com’, ‘subject’: u’Hello’}

Nuestro formulario de contacto trata sólo con cadenas, que son “limpiadas” a objetos Unicode, pero si tuviéramos que utilizar un IntegerField o un DateField, el marco de formularios garantizaría que cleaned_data utiliza enteros Python adecuados u objetos datetime.date para los campos dados.

django-rest-swagger

Vincular objetos Form en vistas.

Ahora que tiene algunos conocimientos básicos sobre las clases Form, veremos cómo podemos utilizar esta infraestructura para sustituir algo en nuestra vista contact(). He aquí cómo podemos reescribir contacto() para utilizar el marco de formularios:

# views.py

from django.shortcuts import render_to_response
from mysite.contact.forms import ContactForm

def contact(request):

if request.method == ‘POST’:

form = ContactForm(request.POST)
if form.is_valid():

cd = form.cleaned_data
send_mail(

cd['subject'],
cd['message'],
cd.get(‘e-mail’, ‘noreply@example.com’),
['siteowner@example.com'],

)
return HttpResponseRedirect(‘/contact/thanks/’)

else:

form = ContactForm()
return render_to_response(‘contact_form.html’, {‘form’: form})

# contact_form.html

<html>
<head>

<title>Contact us</title>

</head>
<body>

<h1>Contact us</h1>
{% if form.errors %}

<p style=”color: red;”>

Please correct the error{{ form.errors|pluralize }} below.

</p>

{% endif %}
<form action=”" method=”post”>

<table>

{{ form.as_table }}

</table>
<input type=”submit” value=”Submit”>

</form>

</body>
</html>

El marco de formularios de Django gestiona la pantalla del HTML, la validación, la limpieza de los datos, y el volver a mostrar el formulario con errores.

Trate de ejecutar esto en local. Cargue el formulario, emítalo sin rellenar ningun campo, emítalo con una dirección inválida de correo electrónico, y finalmente emítalo con los datos válidos.

 

Cambiar como se renderizan los campos.

Probablemente lo primero que notará cuando renderiza el formulario localmente es que el campo mensaje se muestra como un <input type=”text”>, y debería ser un <textarea>. Podemos arreglar esto mediante el establecimiento del widget del campo:

from django import forms

class ContactForm(forms.Form):

subject = forms.CharField()
e-mail = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea

El marco de formularios separa la lógica de presentación de cada campo en un conjunto de widgets. Cada tipo de campo tiene un widget por defecto, pero se puede reemplazar la configuración predeterminada o proporcionar un widget personalizado.

 

Piense en las clases Field como la representación de la lógica de validación, mientras que los widgets representan la lógica de presentación.

 

Establecer una longitud máxima.

Una de las necesidades de validación más común es comprobar que un campo es de un tamaño determinado.Mejoremos nuestro formulario de contacto limitando el asunto a 100 caracteres. Para hacer eso, proporcionar un max_length al Charfield, de esta forma:

from django import forms

class ContactForm(forms.Form):

subject = forms.CharField(max_length=100)
e-mail = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)

Un argumento opcional min_length también está disponible.

 

Establecer valores iniciales.

Como una mejora a este formulario, vamos a añadir un valor inicial para el campo Asunto: “I love your site! “. Para hacer esto, podemos utilizar el argumento initial cuando creamos una instancia Form:

def contact(request):

if request.method == ‘POST’:

form = ContactForm(request.POST)
if form.is_valid():

cd = form.cleaned_data
send_mail(

cd['subject'],
cd['message'],
cd.get(‘e-mail’, ‘noreply@example.com’),
['siteowner@example.com'],

)
return HttpResponseRedirect(‘/contact/thanks/’)

else:

form = ContactForm(initial={‘subject’: ‘I love your site!’})
return render_to_response(‘contact_form.html’, {‘form’: form})

Tenga en cuenta que hay una diferencia entre pasar datos inicialmente y pasar datos que se ligan al formulario. Si sólo pasa los datos iniciales, el formulario será independiente, lo que significa que no tendrá ningún mensaje de error.

 

Añadir reglas de validación personalizadas.

Decidimos adoptar una nueva política de validación: cuatro palabras o más, por favor.

Hay varias maneras de conectar la validación personalizada a un formulario Django. Si nuestra regla es algo que volverá a utilizar una y otra vez, podemos crear un tipo de campo personalizado. La mayoría de las validaciones personalizadas son asuntos de una sola vez, sin embargo, y puede vincularse directamente a la clase Form.

Queremos validación adicional en el campo del mensaje, por lo que añadimos un método clean_message() a nuestra clase Form:

from django import forms

class ContactForm(forms.Form):

subject = forms.CharField(max_length=100)
e-mail = forms.EmailField(required=False)
message = forms.CharField(widget=forms.Textarea)

def clean_message(self):

message = self.cleaned_data['message']
num_words = len(message.split())
if num_words < 4:

raise forms.ValidationError(“Not enough words!”)

return message

El sistema de formularios de Django busca automáticamente cualquier método cuyo nombre empieza con clean_ y termina con el nombre de un campo. Si existe alguno, se llama durante la validación.

 

En concreto, el método clean_message() será llamado después de la lógica de validación por defecto para un campo determinado (en este caso, la lógica de validación para un CharField obligatorio).Debido a que los datos del campo ya han sido parcialmente procesados, hay que sacarlos de self.cleaned_data. Además, no tiene que preocuparse de comprobar que el valor existe y no es vacío, el validador lo hace de forma predeterminada.

 

Nosotros, utilizamos una combinación de len() y split() para contar el número de palabras. Si el usuario ha introducido muy pocas palabras, lanzamos un forms.ValidationError. La cadena adjunta a la excepción se muestra al usuario como un elemento de la lista de errores.

 

Es importante que devolvamos explícitamente el valor limpiado para el campo al final del método. Esto nos permite modificar el valor (o convertirlo a un tipo diferente de Python) dentro de nuestro método de validación personalizado. Si olvidamos de la instrucción de retorno, entonces será devuelto None y el valor original se perderá.

 

Especificar etiquetas.

Por defecto, las etiquetas del formulario HTML autogenerado por Django se crean mediante la sustitución de los guiones bajos por espacios y capitalizando la primera letra, por lo que la etiqueta para el campo e-mail es “E-mail”. ¿Suena familiar? Es el mismo algoritmo simple que los modelos Django utilizan para calcular los valores verbose_name de los campos por defecto.

Pero, como con los modelos Django, podemos personalizar la etiqueta de un campo determinado. Sólo tiene que utilizar la etiqueta, de este modo:

class ContactForm(forms.Form):

subject = forms.CharField(max_length=100)
e-mail = forms.EmailField(required=False, label=’Your e-mail address’)
message = forms.CharField(widget=forms.Textarea)

 

Personalizar el diseño de los formularios.

Nuestra plantilla contact_form.html usa {{ form.as_table }} para mostrar el formulario, pero podemos visualizarlo de otras formas para conseguir un control más detallado sobre la pantalla.

La forma más rápida para personalizar la presentación de los formularios es con CSS. Las listas de error, en particular, podían realizarse con algunas mejoras visuales, y las listas de error autogeneradas usan <ul class = “errorlist”> precisamente para que se pueda apuntar a ellas con CSS. El siguiente CSS hace realmente que nuestros errores destaquen:

<style type=”text/css”>

ul.errorlist {

margin: 0;
padding: 0;

}

.errorlist li {

background-color: red;
color: white;
display: block;
font-size: 10px;
margin: 0 0 3px;
padding: 4px 5px;

}

</style>

Aunque es conveniente disponer de nuestro HTML de formulario generado para nosotros, en muchos casos, usted deseará reemplazar la representación por defecto.

Cada widget de campo (<input type=”text”>, <select>, <textarea>, etc) pueden ser renderizado individualmente mediante el acceso a {{ form.fieldname }} en la plantilla, y los errores asociados con un campo están disponibles como {{ form.fieldname.errors }}. Con esto en mente, podemos construir una plantilla personalizada para nuestro formulario de contacto con el siguiente código de plantilla:

<html>
<head>

<title>Contact us</title>

</head>
<body>

<h1>Contact us</h1>
{% if form.errors %}

<p style=”color: red;”>

Please correct the error{{ form.errors|pluralize }} below.

</p>

{% endif %}
<form action=”" method=”post”>

<div class=”field”>

{{ form.subject.errors }}
<label for=”id_subject”>Subject:</label>
{{ form.subject }}

</div>

<div class=”field”>

{{ form.e-mail.errors }}
<label for=”id_e-mail”>Your e-mail address:</label>
{{ form.e-mail }}

</div>

<div class=”field”>

{{ form.message.errors }}
<label for=”id_message”>Message:</label>
{{ form.message }}

</div>

<input type=”submit” value=”Submit”>

</form>

</body>
</html>

{{ form.message.errors }} muestra una <ul class=”errorlist”> si hay errores y una cadena en blanco si el campo es válido (o el formulario no está ligado). También puede tratar form.message.errors como un valor booleano o incluso iterar sobre él como una lista. Considere este ejemplo:

<div class=”field{% if form.message.errors %} errors{% endif %}”>

{% if form.message.errors %}

<ul>
{% for error in form.message.errors %}

<li><strong>{{ error }}</strong></li>

{% endfor %}
</ul>

{% endif %}

<label for=”id_message”>Message:</label>
{{ form.message }}

</div>

En el caso de errores de validación, esto añadirá una clase errors al <div> y muestra la lista de errores en una lista desordenada.

 

rest-framework-docs

Ultima versión estable actualizada de Django:

Descarga

Si te ha gustado el artículo inscribete al feed clicando en la imagen más abajo para tenerte siempre actualizado sobre los nuevos contenidos del blog:

Espero que esta publicación te haya gustado. Si tienes alguna duda, consulta o quieras complementar este post, no dudes en escribir en la zona de comentarios. También puedes visitar Facebook, Twitter, Google +, Linkedin, Instagram, Pinterest y Feedly donde encontrarás información complementaria a este blog. COMPARTE EN!

0 comentarios:

Publicar un comentario

Archivo