106
HTUTutorial de Django – I UTH TUIntroducción a Django UT Django es un framework de desarrollo web escrito en Python con el que usted puede construir y mantener aplicaciones web de alta calidad con un mínimo de esfuerzo. TInstalar PythonT Django está escrito completamente en Python, por lo que el primer paso en la instalación del marco es el asegurarse de que tiene Python instalado. HTUhttp://www.python.org/download/ UTH T Instalar DjangoT HTU http://www.djangoproject.com/download/ UTH Linux: sudo python setup.py install Windows: python setup.py install Los archivos de Django se instalarán en el directorio site-packages de su instalación de Python, en donde Python busca las bibliotecas de terceros. TPrueba de la instalación de DjangoT En una shell de comandos, cambie a otro directorio (no el directorio que contiene el directorio de Django) e inicie el intérprete de Python interactivo escribiendo python. Si la instalación fue exitosa, usted debe ser capaz de importar el módulo de Django: >>> import django >>> django.VERSION (1, 1, 0, ‘final’, 1) TEstablecer una base de datosT En este punto, usted podría comenzar a escribir una aplicación Web con Django, porque el único requisito previo es una instalación de Python. Sin embargo, es probable que desarrolle un sitio web controlado por base de datos, en cuyo caso tendrá que configurar un servidor de base de datos. Django es compatible con cuatro motores de base de datos:

Tutorial de Django

Embed Size (px)

Citation preview

Page 1: Tutorial de Django

HTUTutorial de Django – I UTH

TUIntroducción a DjangoUT

Django es un framework de desarrollo web escrito en Python con el que usted puede construir y mantener aplicaciones web de alta calidad con un mínimo de esfuerzo.

TInstalar PythonT

Django está escrito completamente en Python, por lo que el primer paso en la instalación del marco es el asegurarse de que tiene Python instalado.

HTUhttp://www.python.org/download/UTH

TInstalar DjangoT

HTUhttp://www.djangoproject.com/download/UTH

Linux: sudo python setup.py install Windows: python setup.py install

Los archivos de Django se instalarán en el directorio site-packages de su instalación de Python, en donde Python busca las bibliotecas de terceros.

TPrueba de la instalación de DjangoT

En una shell de comandos, cambie a otro directorio (no el directorio que contiene el directorio de Django) e inicie el intérprete de Python interactivo escribiendo python. Si la instalación fue exitosa, usted debe ser capaz de importar el módulo de Django:

>>> import django >>> django.VERSION (1, 1, 0, ‘final’, 1)

TEstablecer una base de datos T

En este punto, usted podría comenzar a escribir una aplicación Web con Django, porque el único requisito previo es una instalación de Python. Sin embargo, es probable que desarrolle un sitio web controlado por base de datos, en cuyo caso tendrá que configurar un servidor de base de datos.

Django es compatible con cuatro motores de base de datos:

Page 2: Tutorial de Django

• PostgreSQL ( HTUhttp://www.postgresql.org/UTH) • SQLite 3 (HTUhttp://www.sqlite.org/UTH) • MySQL ( HTUhttp://www.mysql.com/ UTH) • Oracle (HTUhttp://www.oracle.com/UTH)

La configuración de la base de datos es un proceso de dos pasos:

1. En primer lugar, tendrá que instalar y configurar el servidor de base de datos. 2. En segundo lugar, tendrá que instalar la librería Python para nuestra base de

datos de back-end. Esto es código Python de terceros que permite interactuar con la base de datos.

TUso de Django con MySQLT

Django requiere MySQL 4.0 o superior. Las versiones 3.x no soportan subconsultas anidadas y algunas otras sentencias SQL estándar .

También tendrá que instalar el paquete de MySQLdb desde HTUhttp://www.djangoproject.com/r/python-mysql/UTH. En Linux, puede comprobar si su paquete de distribución del sistema de gestión ofrece un paquete llamado python-mysql, python-MySQLdb, mysql-python, o algo similar.

TIniciar un proyectoT

Una vez que haya instalado Python, Django, y (opcionalmente) el servidor/librería de base de datos, puede dar el primer paso del desarrollo de una aplicación Django mediante la creación de un proyecto.

Un proyecto es una colección de ajustes de una instancia de Django, incluyendo la configuración de la base de datos, las opciones específicas de Django, y la configuración de la aplicación.

Si es la primera vez que usa Django, usted tendrá que tener cuidado con la configuración inicial. Cree un nuevo directorio para comenzar a trabajar, tal vez algo como /home/nombre de usuario/djcode/

Vaya al directorio que ha creado y ejecute el comando siguiente:

django-admin.py startproject mysite

Esto creará un directorio TmysiteT en el directorio actual.

El comando startproject crea un directorio que contiene cuatro archivos:

mysite/

__init__.py manage.py settings.py urls.py

Page 3: Tutorial de Django

• __init__.py: Un archivo necesario para que Python trate el directorio mysite como un paquete (un grupo de módulos de Python). Es un fichero vacío, y normalmente no se le añade nada.

• manage.py: Utilidad de línea de comandos que le permite interactuar con el proyecto Django de diversas maneras. Con python manage.py puede tener una idea de lo que puede hacer. Usted nunca tiene que editar este archivo, sino que se crea en el directorio por pura conveniencia.

• settings.py: Características de configuración de este proyecto Django. Echele un vistazo para tener una idea de los tipos de configuraciones disponibles, junto con sus valores predeterminados.

• urls.py: Las direcciones URL de este proyecto Django. Piense en ello como la “tabla de contenidos” de su sitio Django. Por el momento, está vacío.

A pesar de su pequeño tamaño, estos archivos ya constituyen una aplicación Django de trabajo.

TEjecutar el servidor de desarrollo (runserver)T

El servidor de desarrollo de Django (también llamado runserver debido al comando que lanza) es un servidor web ligero que puede utilizar durante el desarrollo de su sitio. Está incluido con Django para que pueda desarrollar su sitio rápidamente, sin tener que lidiar con la configuración del servidor de producción (Apache, por ejemplo) hasta que esté listo para la producción.

El servidor de desarrollo rastrea su código y automáticamente vuelve a cargarlo, por lo que es fácil para usted cambiar el código sin necesidad de reiniciar nada.

Para iniciar el servidor, vaya al directorio del proyecto si no lo ha hecho, y ejecute este comando:

python manage.py runserver

Esto iniciará el servidor de forma local en el puerto 8000, accesible sólo para las conexiones de su propio equipo. Ahora que está en ejecución, visite Thttp://127.0.0.1:8000/T con su navegador Web. Verá un “Welcome to Django” ¡Funciona!

HTUTutorial de Django – IIUTH

25. febrero 2010 - Visitada 2661 veces, 2 hoy

HTUTécnicoUTH

Page 4: Tutorial de Django

TUVistas y URLconfs UT

Su primera Página Django: Hola Mundo

Como primer objetivo, vamos a crear una página Web que produzca el mensaje de ejemplo famoso: “Hola mundo”.

Con Django, el contenido de la página es producido por una vista, y la dirección se especifica en una URLconf. En primer lugar, vamos a escribir la función de la vista “Hola Mundo”.

Una vista “Hola Mundo” es simple. Aquí está toda la función que debe escribir en el archivo Tviews.py T:

from django.http import HttpResponse

def hello(request):

return HttpResponse(“Hello world”)

Una vista en Python es sólo una función que toma un THttpRequest Tcomo su primer parámetro y devuelve una instancia de THttpResponse T. Para que una función de Python sea una vista Django, tiene que hacer esas dos cosas. (Hay excepciones, pero las veremos más tarde.) Si en este momento ejecuta Tpython manage.py runserverT de nuevo, seguirás viendo el mensaje “Bienvenido a Django”, sin ningún rastro de “Hola mundo”. Eso se debe a que el proyecto mysite no conoce la vista hello; se necesita ahora decirle a Django explícitamente que se va a activar esa vista en una determinada URL, usando URLconf. Un URLconf es como una tabla de contenidos para un sitio web Django. Básicamente, es un mapeo entre las URL y las funciones de vista que deben llamarse para esas direcciones URL. El URLconf por defecto incluye algunas características comentadas de uso común en Django, por lo que la activación de esas características es tan fácil como descomentar las líneas adecuadas. Si se ignora el código comentado, aquí está la esencia de un URLconf:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,

)

Page 5: Tutorial de Django

Lo más importante a destacar es la variable urlpatterns, que Django espera encontrarla en el módulo URLconf. Esta variable define la asignación entre las direcciones URL y el código que controla las direcciones URL. Por defecto, el URLconf está vacío – la aplicación Django está en blanco. Para agregar una URL y una vista al URLconf, simplemente añadir una tupla Python que asigna un patrón de URL a la función de la vista. He aquí cómo conectarlo en la vista hello:

from django.conf.urls.defaults import *

from mysite.views import hello

urlpatterns = patterns(”,

(‘^hello/$’, hello),

)

En pocas palabras, se le dice a Django que cualquier petición a la URL /hello/ debería ser gestionada por la función de vista hello.

Para probar los cambios a la URLconf, inicie el servidor de desarrollo de Django con el comando python manage.py runserver. (Si usted lo dejó ejecutándose también está bien. El servidor de desarrollo detecta automáticamente los cambios en su código Python y vuelve a cargarlos cuando sea necesario, para que no tenga que reiniciar el servidor entre los cambios.) El servidor se está ejecutando en la dirección http://127.0.0.1:8000/, por lo que abra un navegador Web y vaya a http://127.0.0.1:8000/hello/. Usted debe ver el texto “Hola mundo”, la salida de su vista Django. Usted hizo su primera página web en Django.

Una nota rápida sobre errores 404

En este punto, el URLconf define sólo un único patrón de URL: el que trata las solicitudes de la dirección /hello/. ¿Qué sucede cuando usted solicita una dirección URL diferente?

Usted debe ver un mensaje de “Página no encontrada”. Django muestra este mensaje porque ha solicitado una URL que no está definida en su URLconf.

La utilidad de esta página va más allá del mensaje básico de error 404. También le dice, de forma precisa que URLconf Django es usada y cada patrón utilizado en el URLconf. De esa información, usted debe ser capaz de determinar por qué la dirección URL solicitada arrojó un error 404.

Naturalmente, esta información es confidencial y está destinada únicamente a usted, el desarrollador Web. Si esto fuese un sitio de desplegado en Internet, usted no desearía exponer esa información al público. Por esta razón, esta página “Página no encontrada”, se muestra sólo si su Proyecto de Django se encuentra en modo de depuración. Le explicaremos cómo desactivar el modo de depuración más tarde. Por ahora, sólo sepa que cada proyecto Django creado se encuentra en modo de depuración, y si el proyecto no está en modo de depuración, Django emite una respuesta 404 diferente.

Page 6: Tutorial de Django

Una nota rápida sobre la raíz del sitio

Como se explicó en la última sección, verá un mensaje de error 404 si usted va a la raíz del sitio: http://127.0.0.1:8000/. El patrón URL para que coincida con la raíz del sitio es un poco contradictorio, por lo que es digno de mención. Cuando esté listo para poner en práctica una vista de la raíz del sitio, utilice el patrón de URL ‘^$’, que coincide con una cadena vacía. He aquí un ejemplo:

from mysite.views import hello, my_homepage_view

urlpatterns = patterns(”,

(‘^$’, my_homepage_view),

# …

)

Cómo Django procesa una petición:

1. Una petición viene a /hello/. 2. Django determina el URLconf raíz en base a ROOT_URLCONF. 3. Django busca en todos los urlpatterns del URLconf por el primero que coincida

con /hello/. 4. Si encuentra una coincidencia, llama a la función de vista asociada. 5. La función de vista devuelve un HttpResponse. 6. Django convierte el HttpResponse en la respuesta HTTP apropiada, que resulta

en una página web.

Ahora que conoce los aspectos básicos de cómo hacer páginas Django. Es muy sencillo, en realidad: sólo escribir funciones de vista y mapearlas a URLs a través de URLconfs.

Segunda vista: contenido dinámico

Vamos a crear algo más dinámico: una página Web que muestre la fecha y hora actuales. Este es un paso agradable y simple, porque no se trata de una base de datos o cualquier entrada de usuario, sólo la salida del reloj interno del servidor. Es sólo un poco más emocionante que “Hola mundo”, pero se muestran un par de conceptos nuevos.

Para realizar una vista Django que muestre la fecha y hora actuales, sólo es necesario colocar la sentencia datetime.datetime.now() en una vista y devolver un THttpResponseT.

from django.http import HttpResponse

import datetime

def current_datetime(request):

now = datetime.datetime.now()

Page 7: Tutorial de Django

html = “It is now %s.” % now

return HttpResponse(html)

Al igual que con la función de vista hello, esta debe residir en Tviews.pyT. Este es el aspecto completo de Tviews.py T:

from django.http import HttpResponse

import datetime

def hello(request):

return HttpResponse(“Hello world”)

def current_datetime(request):

now = datetime.datetime.now()

html = “It is now %s.” % now

return HttpResponse(html)

Después de añadir esto a Tviews.pyT, agreguar el patrón URL a urls.py para decirle a Django que URL debe manejar esta vista. Algo como /time/ tendría sentido:

from django.conf.urls.defaults import *

from mysite.views import hello, current_datetime

urlpatterns = patterns(”,

(‘^hello/$’, hello),

(‘^time/$’, current_datetime),

)

Con la vista escrita y la URLconf actualizada, arrancar el runserver y visitar http://127.0.0.1:8000/time/ en el navegador. Usted debería ver la fecha y hora actuales.

URLconfs y acoplamiento débil

Ahora es un buen momento para destacar una filosofía clave detrás de URLconfs y detrás de Django en general: el principio de acoplamiento débil. Si dos trozos de código están débilmente acoplados, los cambios realizados a uno de ellos tendrá poco o ningún efecto en el otro.

Las URLconfs de Django son un buen ejemplo de este principio en la práctica. En una aplicación Web Django, las definiciones de URL y las funciones de vista que ellas

Page 8: Tutorial de Django

llaman están débilmente acopladas, es decir, la decisión de lo que la URL debe ser para una determinada función y la implementación de la función residen en dos lugares diferentes. Esto le permite intercambiar una pieza sin afectar a la otra.

Por ejemplo, considere la vista del current_datetime. Si usted quiere cambiar la dirección por ejemplo, para moverla de /time/ a /current-time/ podría hacer un cambio rápido en URLconf sin tener que preocuparse por la vista. Del mismo modo, si usted quisiera cambiar la función de vista que altera la lógica de alguna manera, podría hacerlo sin afectar a la URL a la que está vinculada la función.

Además, si usted quiere exponer a la actual funcionalidad de la fecha en varias URL, usted fácilmente podría hacerlo editando URLconf, sin tener que tocar el código de la vista. En este ejemplo, el current_datetime está disponible en dos URLs. Es un ejemplo artificial, pero esta técnica puede ser útil:

urlpatterns = patterns(”,

(‘^hello/$’, hello),

(‘^time/$’, current_datetime),

(‘^another-time-page/$’, current_datetime),

)

Las URLconfs y las vistas están débilmente acopladas en la acción.

Tercera vista: URLs dinámicas

En la vista current_datetime, el contenido de la página, la fecha/hora actual, es dinámica, pero la dirección (/time/) es estática. En la mayoría de aplicaciones Web dinámicas, sin embargo, una dirección URL contiene los parámetros que influyen en la salida de la página. Por ejemplo, una librería on-line podría dar a cada libro su propia URL.

Crearemos una tercera vista que muestre la fecha y hora actuales compensada por un cierto número de horas. El objetivo es diseñar un sitio para que la página /time/plus/1/ muestre la fecha y hora dentro de una hora, la página /time/plus/2/ muestra la fecha y hora dentro de dos horas, la página /time/plus/3/ muestre la fecha y hora dentrop de tres horas, y así sucesivamente.

Un principiante podría pensar en codificar una función de vista distinta para cada desplazamiento de hora, que podría dar lugar a una URLconf como:

urlpatterns = patterns(”,

(‘^time/$’, current_datetime),

(‘^time/plus/1/$’, one_hour_ahead),

Page 9: Tutorial de Django

(‘^time/plus/2/$’, two_hours_ahead),

(‘^time/plus/3/$’, three_hours_ahead),

(‘^time/plus/4/$’, four_hours_ahead),

)

Entonces, ¿cómo diseñar la aplicación para manejar los desplazamiento de hora arbitrarios? La clave es usar comodines de patrones URL. Como se mencionó anteriormente, un patrón URL es una expresión regular, por lo que puede utilizar el patrón de expresión regular \d+ para que coincida con uno o más dígitos:

urlpatterns = patterns(”,

# … (r’^time/plus/\d+/$’, hours_ahead),

# …

)

Este nuevo patrón URL casará con cualquier URL como /time/plus/2/, /time/plus/25/, o incluso /time/plus/100000000000/. Ahora, vamos a limitarlo de forma que se permita un desplazamiento máximo de 99 horas. Esto significa que queremos permitir, números de uno o de dos dígitos, y en la sintaxis de la expresión regular, que se traduce en \d(1,2):

(r’^time/plus/\d{1,2}/$’, hours_ahead),

Un detalle importante que se introduce aquí es el carácter r al principio de la expresión regular. Este caracter le dice a Python que la cadena es una “raw string” – su contenido no debe interpretar barras invertidas. En las cadenas normales de Python, las barras invertidas son usadas para caracteres de escape especiales, como la cadena ‘\n’, que es una cadena de caracteres que contiene una nueva línea.

Cuando se agrega el r para que sea una raw string, Python, no aplica el escape de la barra invertida, por lo que r’\n’ es una cadena de dos caracteres que contiene una barra invertida literal y la n minúscula. Se recomienda fuertemente que utilice raw string en cualquier momento si está definiendo una expresión regular en Python. A partir de ahora, todos los urlpatterns en este libro serán raw string.

Ahora que se ha designado un comodín para la dirección, usted necesita una manera de pasar esos datos de comodín a la función de vista, de modo que usted pueda utilizar una función de vista única para cualquier desplazamiento de hora arbitrario. Usted puede hacer esto colocando entre paréntesis los datos de la URLpattern que desea guardar. En el caso del ejemplo, lo que desea guardar es cualquier número que se introduzca en la URL, así que ponga paréntesis alrededor de \d(1,2), de esta manera:

(r’^time/plus/(\d{1,2})/$’, hours_ahead),

Page 10: Tutorial de Django

Usted está utilizando paréntesis para capturar datos del texto concordante. El URLconf final, incluidos los últimos dos puntos de vista, se parece a esto:

from django.conf.urls.defaults import *

from mysite.views import hello, current_datetime, hours_ahead

urlpatterns = patterns(”,

(r’^hello/$’, hello),

(r’^time/$’, current_datetime),

(r’^time/plus/(\d{1,2})/$’, hours_ahead),

)

hours_ahead es muy similar a la vista current_datetime escrita antes, con una diferencia clave: que lleva un argumento extra que el número de horas de desplazamiento. Aquí está la vista de código:

from django.http import Http404, HttpResponse

import datetime

def hours_ahead(request, offset):

try:

offset = int(offset)

except ValueError:

raise Http404()

dt = datetime.datetime.now() + datetime.timedelta(hours=offset)

html = “In %s hour(s), it will be %s.” % (offset, dt)

return HttpResponse(html)

Con esta función de vista y el URLconf escrito, iniciar el servidor de desarrollo de Django (si no está ya en ejecución), y visitar http://127.0.0.1:8000/time/plus/3/ para verificar que funciona. A continuación, intentar http://127.0.0.1:8000/time/plus/5/. Luego http://127.0.0.1:8000/time/plus/24/. Por último, visitar http://127.0.0.1:8000/time/plus/100/ para comprobar que el patrón en el URLconf acepta números sólo de uno o dos dígitos; Django debería mostrar un error de “Página no encontrada” en este caso, tal y como vimos. La URL http://127.0.0.1:8000/time/plus/ (sin horas) también debería lanzar un error 404.

Page 11: Tutorial de Django

Páginas de error bonitas en Python

Vamos a introducir deliberadamente un error de Python comentando en el archivo views.py las líneas offset=int(offset) en la vista de hours_ahead:

def hours_ahead(request, offset):

# try:

# offset = int(offset)

# except ValueError:

# raise Http404()

dt = datetime.datetime.now() + datetime.timedelta(hours=offset)

html = “In %s hour(s), it will be %s.” % (offset, dt)

return HttpResponse(html)

Inicie el servidor de desarrollo y vaya a /time/plus/3/. Usted verá una página de error con mucha información, incluido un mensaje TypeError que aparece en la parte superior: “unsupported type for timedelta hours component: unicode”

¿Qué pasó? Pues bien, la función de datetime.timedelta espera que el parámetro de horas que se le pase sea un número entero, y el trozo de código que convierte el desplazamiento a un número entero fue comentado. Eso provoca que datetime.timedelta lance un TypeError. Es el típico pequeño fallo que todos los programadores han cometido en algún momento.

El objetivo de este ejemplo es mostrar las páginas de error de Django. Tómese su tiempo para explorar la página de error y conocer la distinta información que ofrece.

La página de error de Django es capaz de mostrar más información en ciertos casos especiales, como el caso de errores de sintaxis de plantilla. Los veremos más tarde, cuando hablemos del sistema de plantillas de Django. Por el momento, descomente las líneas del offset = int (offset) para obtener la función de vista funcionando correctamente de nuevo.

HTUTutorial de Django – III UTH

29. marzo 2010 - Visitada 1660 veces, 2 hoy

HTUTécnicoUTH

Page 12: Tutorial de Django

TUPlantillas – Templates UT

No es una buena idea codificar el HTML directamente en las vistas.

Es mucho más limpio y más fácil de mantener Tseparar el diseño de la página del código PythonT. Podemos hacer esto con el sistema de plantillas de Django.

Base del Sistema de Plantillas

Una plantilla de Django es una cadena de texto para separar la presentación de un documento de sus datos. Una plantilla define contenedores y varios bits de lógica básica (etiquetas) que regulan la forma en que el documento debe ser mostrado. Por lo general las plantillas se utilizan para producir HTML, pero las plantillas Django plantillas son igualmente capaces de generar cualquier otro formato basado en texto.

Comencemos con una plantilla sencilla de ejemplo. Esta plantilla Django describe una página HTML que, da las gracias a una persona por hacer un pedido a la empresa.

<html>

<head><title>Ordering notice</title></head>

<body>

<h1>Ordering notice</h1>

<p>Dear {{ person_name }},</p>

Thanks for placing an order from {{ company }}. It’s scheduled to ship on {{ ship_date|date:”F j, Y” }}.

Here are the items you’ve ordered: <ul> {% for item in item_list %} <li>{{ item }}</li> {% endfor %} </ul> {% if ordered_warranty %}

Your warranty information will be included in the packaging.

{% else %}

Page 13: Tutorial de Django

You didn’t order a warranty, so you’re on your own when the products inevitably stop working.

{% endif %}

Sincerely, {{ company }}

Esta plantilla es HTML básico con algunas variables y etiquetas de plantilla dentro de ella. Cualquier texto rodeado por un par de llaves (por ejemplo, {{ person_name }}) es una variable. Esto significa “insertar el valor de la variable con el nombre dado.” ¿Cómo podemos especificar los valores de las variables?Llegaremos a eso después.

Cualquier texto que está rodeado de llaves y porcentaje (por ejemplo, {% if ordered_warranted %}) es una etiqueta de plantilla. La definición de una etiqueta es bastante amplia: una etiqueta sólo le dice al sistema de plantillas “haz algo”.

Por último, el segundo párrafo de esta plantilla contiene un ejemplo de filtro, que es la forma más conveniente de modificar el formato de una variable. En este ejemplo, {{ ship_date | date: “F j, Y” }}, estamos pasandole a la variable ship_date el filtro date, dándole al filtro date el argumento “F j, Y”. El formato de filtro date formatea las fechas a un formato dado, como se especifica en el argumento. Los filtros son adjuntados con un carácter de canalización (|).

Cada plantilla Django tiene acceso a varias etiquetas y filtros incorporados, muchos de los cuales serán discutidos en las siguientes secciones.

Usando el sistema de plantillas

Entremos en el sistema de plantillas de Django para que cómo funciona, pero no estamos todavía para integrarlo con las vistas que hemos creado en el capítulo anterior. Nuestro objetivo aquí es Tmostrar cómo funciona el sistema independiente del resto de DjangoT. (Por lo general vamos a usar el sistema de plantillas dentro de una vista de Django, pero hay que dejar claro que el sistema de plantillas es sólo una librería de Python que se puede utilizar en cualquier lugar, no sólo en las vistas de Django.)

Esta es la forma más básica en que se puede utilizar el sistema de plantillas de Django:

1. Crear un objeto Template proporcionando el código de la plantilla raw como una cadena.

2. Llamar al método render () del objeto Template con un conjunto de variables (el contexto). Esto devuelve una plantilla renderizada completamente como una cadena, con todas las variables y etiquetas de plantilla evaluadas de acuerdo al contexto.

>>> from django import template >>> t = template.Template(‘My name is {{ name }}.’) >>> c = template.Context({‘name’: ‘Adrian’}) >>> print t.render(c) My name is Adrian.

Page 14: Tutorial de Django

>>> c = template.Context({‘name’: ‘Fred’}) >>> print t.render(c) My name is Fred.

Crear objetos Template

La forma más fácil de crear un objeto TTemplateT es crear una instancia directamente. La clase TTemplateT reside en el módulo django.template, y el constructor toma un argumento, el código de plantilla raw.

Veamos algunos aspectos básicos del sistema de plantillas:

>>> from django.template import Template >>> t = Template(‘My name is {{ name }}.’) >>> print t

Si lo estamos haciendo de forma interactiva, veremos algo como esto:

<django.template.Template object at 0xb7d5f24c>

Cuando se crea un objeto Template, el sistema de plantillas compila el código de la plantilla raw internamente de forma optimizada, preparándolo para el renderizado. Pero si el código de la plantilla incluye cualquier error de sintaxis, la llamada a Template() producirá una excepción TTemplateSyntaxErrorT.

Renderizar un Template

Una vez que tiene un objeto Template, puede pasarle datos, dándole un contexto. Un contexto es simplemente un conjunto de nombres de variables de plantilla y sus valores asociados. Una plantilla usa un contexto para rellenar sus variables y evaluar sus etiquetas.

Un contexto es representado en Django por la clase TContextT, que reside en el módulo django.template. Su constructor toma un argumento opcional: un diccionario que mapea los nombres de variables a los valores de las variables. Llamar al método render() del objeto Template con el contexto para rellenar la plantilla:

>>> from django.template import Context, Template >>> t = Template(‘My name is {{ name }}.’) >>> c = Context({‘name’: ‘Stephane’}) >>> t.render(c) u’My name is Stephane.’

Debemos señalar aquí que el valor de retorno de t.render(c) es un objeto Unicode – no una cadena Python normal. Django utiliza objetos Unicode en lugar de cadenas normales en todo el framework.

He aquí un ejemplo de compilar y renderizar una plantilla, usando una plantilla similar a la ejemplo del principio de este capítulo:

Page 15: Tutorial de Django

>>> from django.template import Template, Context >>> raw_template = “”"

Dear {{ person_name }},

… … <p>Thanks for placing an order from {{ company }}. It’s scheduled to … ship on {{ ship_date|date:”F j, Y” }}.</p>

… … {% if ordered_warranty %} … <p>Your warranty information will be included in the packaging.</p>

… {% else %} … <p>You didn’t order a warranty, so you’re on your own when … the products inevitably stop working.</p>

… {% endif %} … … <p>Sincerely, <br/>{{ company }} <p> “”" >>> t = Template(raw_template) >>> import datetime >>> c = Context({‘person_name’: ‘John Smith’, … ‘company’: ‘Outdoor Equipment’, … ‘ship_date’: datetime.date(2009, 4, 2), … ‘ordered_warranty’: False}) >>> t.render(c) u” <p>Dear John Smith,</p>\n\n<p>Thanks for placing an order from Outdoor Equipment. It’s scheduled to\nship on April 2, 2009.</p>\n\n\n<p>You didn’t order a warranty, so you’re on your own when\nthe products inevitably stop working.</p>\n\n\n<p>Sincerely,Outdoor Equipment</p>”

1. Primero importamos las clases Template y Context, ambas residen en el módulo django.template.

2. Guardamos el texto raw de nuestra plantilla en la variable raw_template. Tenga en cuenta que usamos comillas triples para designar a la cadena, ya que se extiende por varias líneas, por contra, las cadenas entre comillas sencillas no pueden ser envueltas en varias líneas.

3. A continuación creamos un objeto plantilla, t, pasando raw_template al constructor de la clase Template.

4. Importamos el módulo de fecha y hora de la librería estándar de Python, porque la necesitaremos en la siguiente declaración.

5. Creamos un objeto Context, c. El constructor de Context, toma un diccionario Python, que mapea los nombres de variable a los valores. Aquí, por ejemplo, se especifica que persona_name es ‘John Smith’, company es ‘Outdoor Equipment “, y así sucesivamente.

6. Por último, llamamos al método render () de nuestro objeto plantilla, pasándole el contexto. Esto devuelve la plantilla renderizada, es decir, que reemplaza las variables de la plantilla con los valores actuales de las variables, y ejecuta cualquier etiqueta de la plantilla.

Page 16: Tutorial de Django

Esos son los fundamentos del uso del sistema de plantillas de Django: simplemente Tescribir una cadena de plantilla, crear un objeto Template, crear un Context, y llamar al método render().T

Múltiples contextos, misma plantilla

Una vez que tiene un objeto Template, usted puede renderizar múltiples contextos a través de él. Considere este ejemplo:

>>> from django.template import Template, Context >>> t = Template(‘Hello, {{ name }}’) >>> print t.render(Context({‘name’: ‘John’})) Hello, John >>> print t.render(Context({‘name’: ‘Julie’})) Hello, Julie >>> print t.render(Context({‘name’: ‘Pat’})) Hello, Pat

Cada vez que usted está utilizando la misma plantilla para renderizar múltiples contextos, es más eficiente crear el objeto plantilla una vez, y luego llamar a render() sobre él varias veces:

# Bad for name in (‘John’, ‘Julie’, ‘Pat’): t = Template(‘Hello, {{ name }}’) print t.render(Context({‘name’: name}))

# Good t = Template(‘Hello, {{ name }}’) for name in (‘John’, ‘Julie’, ‘Pat’): print t.render(Context({‘name’: name}))

Búsqueda de variable de Contexto

En los ejemplos hasta ahora, hemos pasado valores simples a los contextos – en su mayoría cadenas, además de un datetime.date. Sin embargo, Tel sistema de plantillas maneja estructuras de datos complejas, tales como listas, diccionarios, y objetos personalizados T.

La clave para atravesar estructuras complejas de datos en las plantillas Django es el Tcarácter de punto (.)T. Utilice un punto para acceder a las claves del diccionario, atributos, métodos, o índices de un objeto.

Por ejemplo, supongamos que usted está pasando un diccionario Python a una plantilla. Para acceder a los valores de ese diccionario por clave de diccionario, use el punto:

>>> from django.template import Template, Context >>> person = {‘name’: ‘Sally’, ‘age’: ’43′} >>> t = Template(‘{{ person.name }} is {{ person.age }} years old.’)

Page 17: Tutorial de Django

>>> c = Context({‘person’: person}) >>> t.render(c) u’Sally is 43 years old.’

Del mismo modo, los puntos también permiten el acceso a los atributos de los objetos. Por ejemplo, un objeto datetime.date de Python tiene atributos año, mes, día, y puede utilizar un punto para acceder a esos atributos en una plantilla Django:

>>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year 1993 >>> d.month 5 >>> d.day 2 >>> t = Template(‘The month is {{ date.month }} and the year is {{ date.year }}.’) >>> c = Context({‘date’: d}) >>> t.render(c) u’The month is 5 and the year is 1993.’

Este ejemplo utiliza una clase personalizada, lo que demuestra que los puntos permiten también el acceso a atributos de objetos arbitrarios:

>>> from django.template import Template, Context >>> class Person(object):

def __init__(self, first_name, last_name):

self.first_name, self.last_name = first_name, last_name

>>> t = Template(‘Hello, {{ person.first_name }} {{ person.last_name }}.’) >>> c = Context({‘person’: Person(‘John’, ‘Smith’)}) >>> t.render(c) u’Hello, John Smith.’

Los puntos también puede hacer referencia a métodos de objetos. Por ejemplo, cada cadena Python tiene los métodos upper() y isdigit(), y usted puede llamarlos en las plantillas Django, usando la misma sintaxis del punto:

>>> from django.template import Template, Context >>> t = Template(‘{{ var }}—{{ var.upper }}—{{ var.isdigit }}’) >>> t.render(Context({‘var’: ‘hello’})) u’hello—HELLO—False’ >>> t.render(Context({‘var’: ’123′})) u’123—123—True’

Page 18: Tutorial de Django

Tenga en cuenta que no se incluyen los paréntesis en las llamadas a métodos. Además, no es posible pasar argumentos a los métodos, sólo puede llamar a métodos que no requieren argumentos.

Por último, los puntos también se utilizan para acceder a los índices de lista, como en este ejemplo:

>>> from django.template import Template, Context >>> t = Template(‘Item 2 is {{ items.2 }}.’) >>> c = Context({‘items’: ['apples', 'bananas', 'carrots']}) >>> t.render(c) u’Item 2 is carrots.’

Las búsquedas de puntos se pueden resumir así: cuando la plantilla se encuentra con un punto en un nombre de variable, trata las búsquedas siguientes, en este orden:

• Diccionario (por ejemplo, foo["bar"]) • Atributo (por ejemplo, foo.bar) • Llamada a método (por ejemplo, foo.bar()) • Índice de Lista (por ejemplo, foo[2])

El sistema utiliza el tipo de búsqueda que funciona primero. Es una falta de lógica del circuito. Las búsquedas del punto se pueden anidar varios niveles de profundidad. Por ejemplo, el ejemplo siguiente utiliza {{ person.name.upper() }}, que se traduce en una búsqueda de diccionario (person['name']) y luego una llamada al método (upper()):

>>> from django.template import Template, Context >>> person = {‘name’: ‘Sally’, ‘age’: ’43′} >>> t = Template(‘{{ person.name.upper }} is {{ person.age }} years old.’) >>> c = Context({‘person’: person}) >>> t.render(c) u’SALLY is 43 years old.’

Comportamiento de llamada a método

Las llamadas a los métodos son un poco más complejas que el de otros tipos de búsqueda. Aquí hay algunas cosas a tener en cuenta.

Si, durante la búsqueda de métodos, un método produce una excepción, la excepción se propagará a menos que la excepción tenga un atributo silent_variable_failure a True. Si la excepción tiene un atributo silent_variable_failure, la variable se renderizará como una cadena vacía, como en este ejemplo:

>>> t = Template(“My name is {{ person.first_name }}.”) >>> class PersonClass3:

def first_name(self):

Page 19: Tutorial de Django

raise AssertionError, “foo”

>>> p = PersonClass3() >>> t.render(Context({“person”: p})) Traceback (most recent call last): … AssertionError: foo >>> class SilentAssertionError(AssertionError): … silent_variable_failure = True >>> class PersonClass4: …

def first_name(self):

raise SilentAssertionError

>>> p = PersonClass4() >>> t.render(Context({“person”: p})) u’My name is .’

Una llamada al método sólo funcionará si el método no requiere argumentos. De lo contrario, el el sistema se moverá al siguiente tipo de búsqueda (índice de lista). Obviamente, algunos métodos tienen efectos colaterales y sería absurdo, y, posiblemente, incluso un agujero de seguridad, permitir al sistema de plantillas acceder a ellos. Digamos, por ejemplo, que tiene un objeto BackAccount que tiene un método delete(). Si una plantilla incluye algo como {{ account.delete() }}, donde account es un objeto BankAccount, el objeto sería eliminado al procesar la plantilla. Para evitar esto, establezca el atributo de función alters_data en el método:

def delete(self):

# Delete the account

delete.alters_data = True

El sistema de plantillas no ejecutará cualquier método marcado de esta manera. Continuando con el ejemplo actual, si una plantilla incluye {{ account.delete }} y el método delete() tienen alters_data = True, entonces el método delete() no se ejecutará cuando la plantilla se renderice. En su lugar, se producirá un error silencioso.

¿Cómo se gestionan las variables inválidas?

Por defecto, si una variable no existe el sistema de plantillas la renderiza como una cadena vacía, error silencioso. Considere este ejemplo:

¡

Page 20: Tutorial de Django

>>> from django.template import Template, Context >>> t = Template(‘Your name is {{ name }}.’) >>> t.render(Context()) u’Your name is .’ >>> t.render(Context({‘var’: ‘hello’})) u’Your name is .’ >>> t.render(Context({‘NAME’: ‘hello’})) u’Your name is .’ >>> t.render(Context({‘Name’: ‘hello’})) u’Your name is .’

Jugar con objetos Context

La mayoría de las veces, usted instanciará objetos TContextT pasando un diccionario totalmente lleno a Context(). Pero usted también puede añadir y eliminar elementos de un objeto Context, una vez instanciado,usando la sintaxis estándar de Python diccionario:

>>> from django.template import Context >>> c = Context({“foo”: “bar”}) >>> c['foo'] ‘bar’ >>> del c['foo'] >>> c['foo'] Traceback (most recent call last): … KeyError: ‘foo’ >>> c['newvariable'] = ‘hello’ >>> c['newvariable'] ‘hello’

Etiquetas y Filtros de Plantilla Básica

TEtiquetasT

Tif / elseT

La etiqueta {% if %} evalúa una variable, y si esa variable es True (es decir, que existe, no está vacío, y no es un valor boolean FALSE), el sistema mostrará todo entre {% if %} y {% endif %}, como en este ejemplo:

{% if today_is_weekend %}

Welcome to the weekend!

{% endif %}

Una etiqueta {% else %} es opcional:

{% if today_is_weekend %}

Page 21: Tutorial de Django

Welcome to the weekend!

{% else %}

Get back to work.

{% endif %}

TforT

La etiqueta {% for %} permite un bucle sobre cada elemento de una secuencia. La plantilla renderizará todo entre {% for %} y {% endfor %}.

Por ejemplo, podría utilizar el siguiente ejemplo para mostrar una lista de atletas dada una variable athlete_list:

<ul> {% for athlete in athlete_list %}

<li>{{ athlete.name }}</li>

{% endfor %} </ul>

y al revés:

{% for athlete in athlete_list reversed %} … {% endfor %}

Es posible anidar etiquetas {% for %}.

La etiqueta for soporta una clausula opcional {% empty %} que le permite definir que salida si la lista está vacía.

{% for athlete in athlete_list %}

{{ athlete.name }}

{% empty %}

There are no athletes. Only computer programmers.

{% endfor %}

No hay soporte para opciones del tipo break y continue.

Dentro de cada bucle {% for %} usted puede acceder a una variable llamada forloop. Esta variable tiene algunas características que le dan información sobre el progreso del bucle:

Page 22: Tutorial de Django

• forloop.counter siempre se establece en un número entero que representa el número de veces que se ha entrado en el bucle.

• forloop.counter0 es como forloop.counter, excepto que es indexado en cero. Su valor será fijado a 0 la primera vez que se entre en el bucle.

• forloop.revcounter siempre se establece en un número entero que representa el número de elementos restantes del bucle.

• forloop.revcounter0 es como forloop.revcounter, excepto que es indexado en cero.

• forloop.first es un valor booleano que se establece a True si esta es la primera iteración del bucle.

• forloop.last es un valor booleano que se establece a True si esta es la última iteración del bucle.

• forloop.parentloop es una referencia al objeto forloop del bucle padre, en caso de bucles anidados.

Tifequal / ifnotequalT

La etiqueta {% ifequal %} compara dos valores y muestra todo entre {% ifequal %} y {% endifequal %} si los valores son iguales. Este ejemplo compara la plantilla de las variables user y currentuser:

{% ifequal user currentuser %}

<h1>Welcome!</h1>

{% endifequal %}

Al igual que {% if %} la etiqueta {% ifequal %} soporta un {% else %} opcional:

{% ifequal section ‘sitenews’ %}

<h1>Site News</h1>

{% else %}

<h1>No News Here</h1>

{% endifequal %}

Sólo las variables de plantilla, cadenas, enteros y números decimales se permiten como argumentos para {% ifequal %}. Estos son ejemplos válidos:

{% ifequal variable 1 %} {% ifequal variable 1.23 %} {% ifequal variable ‘foo’ %} {% ifequal variable “foo” %}

Cualquier otro tipo de variables, tales como diccionarios, listas o booleanos no pueden codificarse en la etiqueta {% ifequal %}. Estos son ejemplos inválidos:

Page 23: Tutorial de Django

{% ifequal variable True %} {% ifequal variable [1, 2, 3] %} {% ifequal variable {‘key’: ‘value’} %}

Si necesita testear si algo es verdadero o falso, use {% if %} en lugar de {% ifequal %}.

TComentarios T

Para designar un comentario usar {# #}.

No pueden usar esta sintaxis para varias líneas. Para ello usar la etiqueta {% comment %}, como esto:

{% comment %} This is a multiline comment. {% endcomment %}

TFiltrosT

Los filtros de plantilla son formas sencillas de alterar el valor de las variables antes de renderizarlas. Los filtros utilizan un carácter de canalización (pipe), como esto:

{{ name|lower }}

Los filtros se pueden encadenar, es decir, que pueden ser usados en conjunto de manera que la salida de un filtro se aplica al siguiente. He aquí un ejemplo que convierte el primer elemento de una lista a mayúsculas:

{{ my_list|first|upper }}

Algunos filtros toman argumentos que vienen después de dos puntos y siempre entre comillas dobles. He aquí un ejemplo:

{{ bio|truncatewords:”30″ }}

Esto muestra las primeras 30 palabras de la variable bio.

Los siguientes son algunos de los filtros más importantes:

• addslashes: Agrega una barra invertida antes de que cualquier barra invertida, comillas simples o comillas dobles. Esto es útil si el texto producido se incluye en una cadena JavaScript.

• date: Formatea una cadena de fecha o fecha/hora de acuerdo a un formato dado como parámetro.

• length: Devuelve la longitud del valor. Para una lista, devuelve el número de elementos. Para una cadena, devuelve el número de caracteres. Funciona en cualquier objeto Python que sabe cómo determinar su propia longitud, es decir, cualquier objeto que tiene un método __len__()

Page 24: Tutorial de Django

Filosofías y limitaciones

Ahora que usted tiene una idea del sistema de plantillas de Django, hay que señalar alguna de sus limitaciones intencionales, junto con algo de filosofía de por qué funciona de la forma en que funciona.

Más que cualquier otro componente de las aplicaciones web, la sintaxis de las plantillas es muy subjetiva, y las opiniones de los programadores varían significativamente.

Con esto en mente, es posible que le interese saber que Django no requiere que usted use su lenguaje de plantillas. Debido a que Django está destinado a ser un completo entorno web que proporcione todas las piezas necesarias para los desarrolladores web para ser productivo, muchas veces es más conveniente el uso del sistema de plantillas de Django que otras librerías de plantillas Python, pero no es un requisito estricto en ningún sentido.

Sin embargo, está claro que tenemos una fuerte preferencia por el lenguaje de plantillas de Django. El sistema de plantillas tiene sus raíces en la forma en que el desarrollo web se hace en el mundo en línea combinado con la experiencia de los creadores de Django. Aquí están algunas de nuestras filosofías:

• La lógica de negocio deben estar separada de la lógica de presentación. Los desarrolladores de Django ven el sistema de plantillas como una herramienta que controla la presentación y la lógica relacionada con la presentación, y eso es todo.

• La sintaxis debe estar desacoplada del HTML/XML. Aunque el sistema de plantillas de Django se utiliza principalmente para producir HTML, su intención es ser tan útil para los formatos no HTML, tales como el texto sin formato.

• Los diseñadores se supone que se sienten cómodos con el código HTML. El sistema de plantillas no está diseñado de manera que las plantillas necesariamente se muestran muy bien en los editores WYSIWYG como Dreamweaver. Django espera que los autores de plantillas estén cómodos editando el HTML directamente.

• Se asume que los diseñadores no son programadores de Python. Los autores del sistema de plantillas reconocen que a menudo las plantillas de las páginas web son escritas por los diseñadores, no por programadores, y por lo tanto no se debe asumir el conocimiento de Python.

• El objetivo no es inventar un lenguaje de programación. El objetivo es ofrecer como mucho la funcionalidad del esquema de programación, tal como la ramificación y la iteración, que es esencial para la toma de decisiones relacionadas con la presentación.

Usando plantillas en vistas

Recordemos la vista Tcurrent_datetimeT en mysite.views. Esto es:

from django.http import HttpResponse import datetime

def current_datetime(request):

Page 25: Tutorial de Django

now = datetime.datetime.now()

html = “It is now %s.” % now

return HttpResponse(html)

Vamos a cambiar esta vista para utilizar el sistema de plantillas de Django. Al principio se podría pensar en hacer algo como esto:

from django.template import Template, Context from django.http import HttpResponse import datetime

def current_datetime(request):

now = datetime.datetime.now()

t = Template(“It is now {{ current_date }}.”)

html = t.render(Context({‘current_date’: now}))

return HttpResponse(html)

Está claro que utiliza el sistema de plantillas, pero no resuelve los problemas que hemos señalado. A saber, la plantilla está incrustada en el código Python, por lo que no se logra una verdadera separación de los datos y la presentación. Vamos a arreglar esto poniendo la plantilla en un archivo separado, que cargará esta vista.

En primer lugar, podría considerar la posibilidad de guardar la plantilla en algún lugar de su sistema de ficheros y usar Python para leer el contenido de la plantilla. Esto es lo que podría parecerse, suponiendo que la plantilla se ha guardado en el archivo /home/djangouser/templates/mytemplate.html:

from django.template import Template, Context from django.http import HttpResponse import datetime

def current_datetime(request):

now = datetime.datetime.now()

# Simple way of using templates from the filesystem.

# This is BAD because it doesn’t account for missing files!

fp = open(‘/home/djangouser/templates/mytemplate.html’)

t = Template(fp.read())

fp.close()

Page 26: Tutorial de Django

html = t.render(Context({‘current_date’: now}))

return HttpResponse(html)

Este enfoque, sin embargo, es poco elegante, por estas razones:

• No maneja el caso de que el archivo falle, como se señala en el código. Si el archivo mytemplate.html no existe o no es legible, la llamada open() lanzará una excepción IOError.

• Codifica a pelo la ubicación de la plantilla. Si usted fuera a utilizar esta técnica para cada función de vista, estaría duplicando las localizaciones de la plantilla – por no mencionar que se trata de escribir mucho.

• Incluye una gran cantidad de código repetitivo aburrido. Tienes cosas mejores que hacer que escribir las llamadas a open(), fp.read(), y fp.close() cada vez que se carga una plantilla.

Para resolver estos problemas, vamos a utilizar Tla carga de plantillas y la herencia de plantillasT.

Carga de Plantillas

Django proporciona un API cómodo y eficaz para la carga de plantillas del sistema de archivos, con el objetivo de eliminar la redundancia, tanto en las llamadas de carga de plantillas como en las plantillas en sí mismas.

Para usar esta API de carga de plantillas, primero tendrá que decirle al marco donde se almacenan las plantillas. El lugar para hacerlo es su archivo de configuración Tsettings.py T que hemos mencionado en el capítulo anterior, cuando se introdujo la propiedad ROOT_URLCONF.

Abra settings.py y encuentre la propiedad TEMPLATE_DIRS. De forma predeterminada, es una tupla vacía, y es probable que contenga algunos comentarios autogenerados:

TEMPLATE_DIRS = (

# Put strings here, like “/home/html/django_templates”

# or “C:/www/django/templates”.

# Always use forward slashes, even on Windows.

# Don’t forget to use absolute paths, not relative paths.

)

Este ajuste le indica al mecanismo de carga de plantillas de Django donde buscar las plantillas. Elija un directorio donde desea almacenar sus plantillas y añadalo a TEMPLATE_DIRS, así:

Page 27: Tutorial de Django

TEMPLATE_DIRS = (

‘/home/django/mysite/templates’,

)

Hay algunas cosas que debe recordar:

• Usted puede especificar cualquier directorio que desee, siempre y cuando el directorio y las plantillas dentro de ese directorio sean legibles por la cuenta de usuario en las que el servidor Web se ejecuta. Se recomienda la creación de un directorio de plantillas dentro de su proyecto (es decir, dentro del directorio que ha creado mysite)

• Si su TEMPLATE_DIRS sólo contiene un directorio, no se olvide de la coma al final de la cadena del directorio. Python requiere comas dentro de tuplas de un solo elemento para eliminar la ambigüedad de la tupla de una expresión en paréntesis.

• Si está en Windows, incluya la letra de la unidad y utilice las barra inclinadas al estilo Unix en lugar de las barras invertidas.

Lo más simple es utilizar rutas absolutas (es decir, rutas de directorios que comienzan en la raíz del sistema de archivos). Si quiere ser un poco más flexible, sin embargo, usted puede construir TEMPLATE_DIRS dinámicamente, como en este ejemplo:

import os.path

TEMPLATE_DIRS = (

os.path.join(os.path.dirname(__file__), ‘templates’).replace(‘\\’,'/’),

)

Este ejemplo utiliza la variable “mágica” de Python __file__, que se ajusta automáticamente al nombre de archivo del módulo de Python en que reside el código. Se pone el nombre del directorio que contiene a settings.py (os.path.dirname), y se une con las plantillas de una manera (os.path.join), y entonces asegura que todo lo que se utiliza sean barras inclinadas en lugar de barras invertidas (en el caso de Windows).

Con TEMPLATE_DIRS activo, el siguiente paso es cambiar el código para el uso de la funcionalidad de la carga de plantillas de Django en lugar de codificar a pelo las rutas de las plantillas. Volviendo a nuestra vista current_datetime, vamos a cambiarla de esta manera:

from django.template.loader import get_template

from django.template import Context from django.http import HttpResponse import datetime

Page 28: Tutorial de Django

def current_datetime(request):

now = datetime.datetime.now()

t = get_template(‘current_datetime.html’)

html = t.render(Context({‘current_date’: now}))

return HttpResponse(html)

La función get_template() toma un nombre de plantilla como argumento, busca dónde la plantilla residee en el sistema de archivos, abre ese archivo, y devuelve un objeto Template compilado.

Si get_template() no puede encontrar la plantilla con el nombre que se le da, lanza una excepción TTemplateDoesNotExistT.

Ahora, crear el archivo current_datetime.html dentro de su directorio de plantillas mediante el siguiente código de plantilla:

It is now {{ current_date }}.

Actualice la página en el explorador Web, y usted debería ver la página completamente renderizada.

render_to_response()

Hemos mostrado cómo cargar una plantilla, rellenar un contexto, y devolver un objeto HttpResponse con el resultado de la plantilla renderizada. Lo hemos optimizado mediante el uso de get_template() en lugar de la codificación a pelo de las plantillas y las rutas de plantillas. Sin embargo, todavía se requiere una buena cantidad de código para escribir todas esas cosas. Debido a que estos pasos son iguales, Django proporciona una Tabreviatura que le permite cargar una plantilla, renderizarla, y devolver un HttpResponse, todo en una sola línea de código T.

Esta abreviatura es una función llamada Trender_to_response()T, que reside en el módulo django.shortcuts. Aquí está el ejemplo current_datetime reescrito para utilizar render_to_response():

from django.shortcuts import render_to_response import datetime

def current_datetime(request):

now = datetime.datetime.now()

return render_to_response(‘current_datetime.html’, {‘current_date’: now})

El primer argumento de render_to_response() es el nombre de la plantilla a usar. El segundo argumento, si lo hay, debe ser un diccionario para usar en la creación de un

Page 29: Tutorial de Django

contexto para la plantilla. Si usted no proporciona un segundo argumento, render_to_response() utiliza un diccionario vacío.

locals()

Muchas veces, usted se encontrará que usted mismo cálcula algunos valores, los almacena en variables (por ejemplo, TnowT en el código anterior), y envía esas variables a la plantilla. Esto es un poco redundante y también significa escribir más.

Usted puede usar la función Python llamada Tlocals()T. Esta Tdevuelve un diccionario que asigna todos los nombres de variables locales a sus valoresT, donde local significa todas las variables que han sido definidas en el ámbito local. Así, la vista anterior podría reescribirse así:

def current_datetime(request):

current_date = datetime.datetime.now()

return render_to_response(‘current_datetime.html’, locals())

Hemos cambiado el nombre de la variable a current_date ahora, ya que ese es el nombre de variable que la plantilla espera.

Subdirectorios en get_template()

Puede ser difícil de manejar el almacenar todas sus plantillas en un solo directorio. Usted podría almacenar plantillas en subdirectorios de su directorio de plantillas, y eso está bien. De hecho, le recomendamos hacerlo; algunas características más avanzadas de Django (como el sistema de vistas genéricas) esperan esta disposición de plantillas por defecto.

t = get_template(‘dateapp/current_datetime.html’)

Ya que render_to_response() es una pequeña envoltura alrededor de get_template(), usted puede hacer lo mismo con el primer argumento de render_to_response(), así:

return render_to_response(‘dateapp/current_datetime.html’, {‘current_date’: now})

La etiqueta de plantilla include

Podemos introducir una etiqueta de plantilla integrada: {% include%}. Esta etiqueta permite incluir el contenido de otra plantilla. El argumento de la etiqueta debe ser el nombre de la plantilla a incluir, y el nombre de la plantilla puede ser una variable o una cadena entre comillas simples o dobles. Cada vez que tenga el mismo código en varias plantillas, considere el uso de {% include %} para eliminar la redundancia.

Estos dos ejemplos incluyen el contenido de la plantilla nav.html. Los ejemplos son equivalentes e ilustran bien el uso de las comillas simples o dobles:

Page 30: Tutorial de Django

{% include ‘nav.html’ %} {% include “nav.html” %}

El siguiente ejemplo incluye el contenido de la plantilla, cuyo nombre figura en la variable template_name:

{% include template_name %}

Al igual que en get_template(), el nombre del fichero de la plantilla se determina mediante la adición al directorio de plantillas de TEMPLATE_DIRS para el nombre de la plantilla.

La plantillas incluidas son evaluadas dentro del contexto de la plantilla que las incluye. Por ejemplo, considere estas dos plantillas:

# mypage.html

{% include “includes/nav.html” %}

<h1>{{ title }}</h1>

# includes/nav.html <div id=”nav”>You are in: {{ current_section }}</div>

Si renderiza mypage.html con un contexto que contiene current_section, entonces la variable estará disponible en la plantilla incluida, como era de esperar.

Si, en una etiqueta {% include %}, no se encuentra una plantilla con el nombre dado, Django hará una de estas 2 cosas:

• Si DEBUG es True, verá una excepción TemplateDoesNotExist en una página de error de Django.

• Si DEBUG es False, la etiqueta fallará de forma silenciosa, no visualizando nada en el lugar de la etiqueta.

Herencia de plantillas

Nuestros ejemplos de plantillas hasta ahora han sido pequeños fragmentos de código HTML, pero en el mundo real usted utilizará el sistema de plantillas de Django para crear páginas enteras de HTML. Esto lleva a un problema de desarrollo web común: a través de un sitio Web, ¿cómo se puede reducir la duplicación y la redundancia de áreas de página comunes, tales como la navegación de todo el sitio?

Una forma clásica de resolver este problema es usar includes de lado servidor, las directivas que usted puede incrustar dentro de sus páginas HTML para incluir una página web dentro de otra. De hecho, Django soporta esta aproximación con la etiqueta {% include %} que acabamos de describir. Pero la forma preferida de resolver este problema con Django es utilizar una estrategia más elegante llamado herencia de plantillas.

Page 31: Tutorial de Django

En esencia, la herencia de plantillas Tle permite construir una plantilla “esqueleto” base que contiene todas las partes comunes de su sitio y define “bloques” que las plantillas hijas puede rellenarT.

Veamos un ejemplo de esto creando una plantilla más completa para nuestra vista Tcurrent_datetimeT, editando el archivo current_datetime.html:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”en”> <head>

<title>The current time</title>

</head> <body>

<h1>My helpful timestamp site</h1>

It is now {{ current_date }}.

<hr>Thanks for visiting my site.

</body> </html>

Eso se ve muy bien, pero ¿qué sucede cuando queremos crear una plantilla para otra vista – por ejemplo, la vista hours_ahead? Si queremos volver a hacer otra plantilla HTML agradable, válida, completa, nos quedará algo como esto:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”en”> <head>

<title>Future time</title>

</head> <body>

<h1>My helpful timestamp site</h1>

In {{ hour_offset }} hour(s), it will be {{ next_time }}.

<hr>Thanks for visiting my site.

</body> </html>

Es evidente que hay mucho HTML duplicado. Imagínese si tuviéramos un sitio más típico, incluyendo una barra de navegación, una hojas de estilo, tal vez algo de JavaScript – empezaremos metiendo todo ese HTML redundante en cada plantilla.

Page 32: Tutorial de Django

La solución de include de lado servidor a este problema es factorizar las partes comunes de ambas plantillas y guardarlos en distintos fragmentos de plantilla, que luego son incluidos en cada plantilla. Tal vez desee guardar la parte superior de la plantilla en un archivo llamado Theader.html T:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”en”> <head>

y quizá almacenar la parte inferior en un fichero llamado Tfooter.htmlT:

<hr>

<p>Thanks for visiting my site.</p>

</body> </html>

Con una estrategia basada en include, los encabezados y los pies de página son fáciles. Es el punto medio el desordenado. En este ejemplo, ambas páginas tienen un título -<h1>My helpful timestamp site </h1> pero ese título no puede encajar en header.html porque el <title> en ambas páginas es diferente. Si incluimos el <h1> en la cabecera, tendríamos que incluir el <title>, que no nos permitiría personalizarlo por página.

El sistema de herencia de plantillas de Django soluciona estos problemas. Puede pensar en ello como una versión dentro-fuera de los includes de lado servidor. En lugar de definir los fragmentos de código que son comunes, se definen los fragmentos de código que son diferentes.

El primer paso es definir una plantilla base, un esqueleto de la página que las plantillas hijas rellenarán después. Aquí hay una plantilla base para nuestro ejemplo en curso:

<!DOCTYPE HTML PUBLIC “-//W3C//DTD HTML 4.01//EN”> <html lang=”en”> <head>

<title>{% block title %}{% endblock %}</title>

</head> <body>

<h1>My helpful timestamp site</h1>

{% block content %}{% endblock %}

{% block footer %}

<hr>

Page 33: Tutorial de Django

<p>Thanks for visiting my site.</p>

{% endblock %}

</body> </html>

Esta plantilla, que llamaremos Tbase.html T, define un documento de esqueleto HTML sencillo que se utilizará para todas las páginas del sitio. Es el trabajo de las plantillas hijas reemplazar, añadir o dejar vacío el contenido de los bloques.

Estamos utilizando una etiqueta de plantilla que no hemos visto antes: la etiqueta {% block %}. Todo lo que hace una etiqueta {% block %} es decirle al motor de plantillas que una plantilla hija puede sustituir aquellas partes de la plantilla.

Ahora que tenemos esta plantilla base, podemos modificar nuestra plantilla Tcurrent_datetime.htmTl existente:

{% extends “base.html” %}

{% block title %}The current time{% endblock %}

{% block content %}

<p>It is now {{ current_date }}.</p>

{% endblock %}

Vamos a crear una plantilla para la vista Thours_aheadT del Capítulo 3. Se podría parecer a esto:

{% extends “base.html” %}

{% block title %}Future time{% endblock %}

{% block content %}

<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>

{% endblock %}

¿No es esto bonito? Cada plantilla contiene sólo el código que es único para esa plantilla. No hay redundancia. Si usted necesita hacer un cambio de diseño de todo el sitio, sólo haga el cambio a Tbase.htmlT, y todas las otras plantillas inmediatamente reflejarán el cambio.

He aquí cómo funciona. Cuando se carga la plantilla Tcurrent_datetime.html T, el motor de plantillas ve la etiqueta {% extends %}, notando que esa plantilla es una plantilla hija. El motor carga inmediatamente la plantilla padre – en este caso, Tbase.html T.

Page 34: Tutorial de Django

En ese momento, el motor de plantillas nota las tres etiquetas {% block %} de base.html y sustituye esos bloques con el contenido de la plantilla hija.

La herencia no afecta al contexto de la plantilla. En otras palabras, cualquier plantilla en el árbol de herencia tendrá acceso a cada una de sus variables de plantilla en el contexto.

Puede utilizar tantos niveles de herencia, según sea necesario. Una forma común de utilizar la herencia es el siguiente enfoque de tres niveles:

1. Crear una plantilla base.html que contenga el diseño principal de su sitio. Esta contiene las cosas que rara vez o nunca se cambian.

2. Crear una plantilla base_SECTION.html por cada sección del sitio (por ejemplo, base_photos.html y base_forum.html). Estas plantillas extienden a base.html e incluyen estilos y diseño específicos de la sección.

3. Crear plantillas individuales para cada tipo de página, como una página del foro o una galería de fotos. Estas plantillas extienden a la plantilla de la sección correspondiente.

Este método maximiza la reutilización de código y facilita el añadir elementos a las zonas comunes, como la sección de navegación.

Aquí hay algunas pautas para trabajar con herencia de plantillas:

• Si usted usa {% extends %} en una plantilla, debe ser la primera etiqueta de esa plantilla.

• Generalmente, cuanto más etiquetas {% block %} en sus plantillas base, mejor. Recordar que las plantillas hijas no tienen que definir todos los bloques de los padres, así que usted puede definir en las plantillas hijas sólo los que necesita.

• Si usted encuentra duplicación de código en varias plantillas, probablemente significa que usted debe mover el código a una etiqueta {% block %} de una plantilla padre.

• Si usted necesita obtener el contenido del bloque de la plantilla padre, use {{ block.super }}, que es una variable “mágica” que proporciona el texto renderizado de la plantilla padre. Esto es útil si desea añadir el contenido de un bloque padre en lugar de sobreescribirlo completamente.

• Usted no puede definir múltiples etiquetas {% block %} con el mismo nombre en la misma plantilla. Esta limitación existe porque una etiqueta de bloque funciona en ambas direcciones. Es decir, un etiqueta de bloque no sólo proporcionan un contenedor para rellenar, sino que también define el contenido que rellena el contenedor en el padre. Si hubiese dos etiquetas {% block %} llamadas iguales en una plantilla, el padre no sabría cual de los contenidos de bloque utilizar.

• El nombre de plantilla que se pasa a {% extends %} se carga utilizando el mismo método que usa get_template(). Es decir, el nombre de la plantilla se añade a la propiedad TEMPLATE_DIRS.

• En la mayoría de los casos, el argumento de {% extends %} será una cadena, pero puede ser una variable, si usted no sabe el nombre de la plantilla padre hasta tiempo de ejecución. Esto le permite hacer algunas cosas de forma dinámica.

Page 35: Tutorial de Django

HTUTutorial de Django – IV UTH

9. abril 2010 - Visitada 1839 veces, 1 hoy

HTUTécnicoUTH

TUModelos UT

Hemos visto los fundamentos de la construcción de sitios Web dinámicos con Django: la configuración de vistas y URLconfs. Como hemos explicado, una vista es responsable de hacer alguna lógica arbitraria, y luego devolver una respuesta. En uno de los ejemplos, nuestra lógica arbitraria fue calcular la fecha y hora actuales.

En las modernas aplicaciones Web, la lógica arbitraria implica a menudo interactúar con una base de datos. Un sitio web controlado por base de datos se conecta a un servidor de base de datos, recupera algunos de los datos, y muestra esos datos en una página Web. El sitio también puede proporcionar medios para que los visitantes rellenen la base de datos por su cuenta.

Django es muy adecuado para hacer sitios web controlados por base de datos porque viene con poderosas herramientas para la realización de consultas de base de datos usando Python. Este capítulo explica esta funcionalidad: Tla capa de base de datos de Django T.

La forma “Tonta” de hacer consultas de base de datos en Vistas

Hay una manera “tonta” de recuperar datos de una base de datos en una vista. Es muy sencillo: sólo tiene que utilizar cualquier librería de Python existente para ejecutar una consulta SQL y hacer algo que con los resultados.

En esta vista de ejemplo, usamos la librería MySQLdb para conectarse a una base de datos MySQL, recuperar algunos registros, y pasarlos a una plantilla para mostrar como una página Web:

from django.shortcuts import render_to_response import MySQLdb

def book_list(request):

db = MySQLdb.connect(user=’me’, db=’mydb’, passwd=’secret’, host=’localhost’) cursor = db.cursor() cursor.execute(‘SELECT name FROM books ORDER BY name’)

Page 36: Tutorial de Django

names = [row[0] for row in cursor.fetchall()] db.close() return render_to_response(‘book_list.html’, {‘names’: names})

Este enfoque funciona, pero algunos problemas saltan a la vista de inmediato:

• Estamos codificando a pelo los parámetros de conexión a la base de datos. Idealmente, esos parámetros deberían almacenarse en la configuración de Django.

• Estamos escribiendo una gran cantidad de código redundante: la creación de una conexión, la creación de un cursor, la ejecución de una sentencia, y el cierre de la conexión. Idealmente, todo lo que tendríamos que hacer es especificar que resultados queremos.

• Esto está ligado a MySQL. Si, cambiamos de MySQL a PostgreSQL, vamos a tener que utilizar un adaptador de base de datos diferente, modificar los parámetros de la conexión, y dependiendo de la naturaleza de la instrucción SQL, posiblemente reescribir el código SQL. Idealmente, el servidor de base de datos que estamos utilizando debería abstraerse, de modo que un cambio de servidor de base de datos podría hacerse en un solo lugar. (Esta característica es especialmente útil si usted está construyendo una aplicación Django de fuente abierta que usted desea que sea usada por tanta gente como sea posible.)

Como es de esperar, la capa de base de datos de Django tiene por objeto resolver estos problemas. He aquí una vista previa de cómo la vista anterior se puede reescribir utilizando la TAPI de base de datos de DjangoT:

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

def book_list(request):

books = Book.objects.order_by(‘name’) return render_to_response(‘book_list.html’, {‘books’: books})

El patrón de desarrollo MTV (o MVC)

Antes de profundizar más en el código, vamos a considerar el diseño general de una aplicación web Django controlada por base de datos.

En las funciones de vista, por ejemplo, hablamos de la importancia de separar la lógica de negocio de la lógica de presentación mediante el uso de un sistema de plantillas. Con la capa de base de datos, aplicaremos la misma filosofía para la lógica de acceso a datos.

Estas tres piezas juntas – lógica de acceso a datos, lógica de negocio, y la lógica de presentación – constituyen un concepto que a menudo se denomina Tpatrón de arquitectura de software Modelo-Vista-Controlador (MVC) T. En este patrón, “Modelo” hace referencia a la capa de acceso a datos, “Vista” hace referencia a la parte del sistema que selecciona qué mostrar y cómo mostrarlo, y “controlador” hace referencia a la parte del sistema que decide qué vista usar, en función de la entrada del usuario, accediendo al modelo cuando sea necesario.

Page 37: Tutorial de Django

Django sigue este patrón MVC lo suficientemente cerca que puede ser denominado un marco MVC. Así es más o menos cómo M, V, y C se descomponen en Django:

• M, la parte de acceso a datos, está a cargo de la capa de base de datos de Django, que se describe en este capítulo.

• V, la parte que selecciona los datos para mostrar y cómo mostrarlo, es manejado por las vistas y las plantillas.

• C, la parte que delega a una vista en función de la entrada del usuario, es manejado por el marco en sí, siguiendo su URLconf y llamando a la función de Python adecuada para la dirección dada.

Debido a que la “C” es manejada por el propio marco y la mayor parte de la emoción en Django se produce en los modelos, plantillas, y vistas, Django ha sido denominado como un marco MTV. En el patrón de desarrollo MTV:

• M significa “modelo”, la capa de acceso a datos. Esta capa todo lo relacionado con los datos: cómo acceder a ellos, cómo validarse, las relaciones entre los datos.

• T representa “Plantilla”, la capa de presentación. Esta capa contiene las decisiones relacionadas con la presentación.

• V significa “Vista”, la capa de lógica de negocio. Esta capa contiene la lógica que accede al modelo ylo remite a la plantilla adecuada(s). Usted puede verlo como el puente entre los modelos y plantillas.

Configuración de la base de datos

En primer lugar, tenemos que realizar alguna configuración inicial, debemos decirle a Django qué servidor de base de datos utilizar y cómo conectarse a él.

Al igual que TEMPLATE_DIRS, la configuración de la base de datos reside en el archivo de configuración de Django, llamado Tsettings.pyT por defecto. Editar el archivo y buscar la configuración de base de datos.

Una vez que haya introducido esa configuración y guarde Tsettings.pyT, es bueno comprobar su configuración. Para ello, ejecute Tpython manage.py shellT, dentro del directorio del proyecto TmysiteT.

>>> from django.db import connection >>> cursor = connection.cursor()

Si no ocurre nada, entonces la base de datos está configurada correctamente. De lo contrario, comprobar el mensaje de error en busca de pistas acerca de lo que está fallando.

Su primera aplicación

Ahora que ha verificado que la conexión funciona, es hora de crear una aplicación Django, un paquete de código Django, que incluya modelos y vistas, que residan juntos en un paquete Python único y que representan una aplicación Django completa.

Page 38: Tutorial de Django

Ya creamos un proyecto en el capítulo 2, ¿cuál es la diferencia entre un proyecto y una aplicación?

• Un proyecto es una instancia de un determinado conjunto de aplicaciones Django, más la configuración de esas aplicaciones.

• Técnicamente, el único requisito de un proyecto es que suministra un archivo de configuración, que define la información de conexión de bases de datos, la lista de aplicaciones instaladas, el TEMPLATE_DIRS, y así sucesivamente.

• Una aplicación es un conjunto portátil de funcionalidad Django, que generalmente incluye modelos y vistas, que residen juntos en un paquete único Python.

• Por ejemplo, Django viene con una serie de aplicaciones, tales como un sistema de comentarios y una interfaz de administración automática. Un aspecto clave sobre estas aplicaciones es que son portables y reutilizables a través de múltiples proyectos.

Si usted está utilizando la capa de base de datos de Django (modelos), debe crear una aplicación Django. Los modelos deben residir dentro de aplicaciones. Así que, para empezar a escribir nuestros modelos, tendremos que crear una aplicación nueva.

En el directorio del proyecto TmysiteT, escriba este comando para Tcrear una aplicación TTbooks T:

python manage.py startapp books

Este comando no produce ningún resultado, pero sí crea un directorio Tbooks T en el directorio TmysiteT.

books/

__init__.py models.py tests.py views.py

Esto es el estado en blanco de su aplicación Django.

Definir modelos en Python

TUn modelo de Django es una descripción de los datos en su base de datos, representada como código Python T. Es su disposición de los datos – el equivalente de sus sentencias SQL CREATE TABLE, excepto que es en Python en lugar de SQL, y que incluye algo más que definiciones de columna de base de datos. Django usa un modelo para ejecutar código SQL y devolver estructuras de datos Python que representan las filas de las tablas de base de datos. Django también utiliza modelos para representar conceptos de alto nivel que SQL no necesariamente puede manejar. Si usted está familiarizado con bases de datos, su primer pensamiento podría ser: ¿No es redundante la definición de modelos de datos en Python en lugar de en SQL? Django funciona de manera que lo hace por varias razones:

Page 39: Tutorial de Django

• A fin de proporcionar una API de acceso a datos conveniente, Django necesita saber el diseño de la bases de datos de alguna manera, y hay dos maneras de lograr esto. La primera es describir explícitamente los datos en Python, y la segunda es la introspección en la base de datos en tiempo de ejecución para determinar los modelos de datos.

• Esta segunda vía parece más limpia, ya que los metadatos residen sólo en un lugar, pero introduce algunos problemas. En primer lugar, la introspección de una base de datos en tiempo de ejecución requiere sobrecarga. Si el marco ha de realizar la introspección cada vez que se procesa una solicitud, incurre en un nivel inaceptable de sobrecarga. En segundo lugar, algunas bases de datos no guardan los suficientes metadatos para una introspección precisa y completa.

• Escribir en Python es divertido, y mantener todo en Python limita el número de veces que su cerebro tiene que hacer un “cambio de contexto” lo que ayuda a la productividad.

• Contar con modelos de datos almacenados como código en lugar de en su base de datos hace que sea más fácil de mantener sus modelos bajo control de versiones. De esta manera, usted puede fácilmente hacer un seguimiento de los cambios en sus diseños de datos.

• SQL permite únicamente un grado determinado de metadatos acerca de un diseño de datos. La mayoría de los sistemas de base de datos, por ejemplo, no ofrecen un tipo de datos especializado para la representación de direcciones de e-mail o URL. Los modelos de Django lo hacen.

• SQL es inconsistente a través de plataformas de base de datos. Si está distribuyendo una aplicación Web, por ejemplo, es mucho mejor distribuir un módulo Python que describa su diseño de datos que conjuntos distintos de sentencias CREATE TABLE para MySQL, PostgreSQL, y SQLite.

Su primer modelo

Nos centraremos en un diseño de datos basado en libro/autor/editor. Usaremos esto como ejemplo, porque las relaciones conceptuales entre libros, autores y editores son bien conocidas.

Vamos a suponer los siguientes conceptos, campos, y relaciones:

• El autor tiene un nombre, un apellido y una dirección de correo electrónico. • Un editor tiene un nombre, una dirección, ciudad, estado o provincia, un país, y

un sitio web. • Un libro tiene un título y una fecha de publicación. También tiene uno o más

autores (relación de muchos a muchos con autores) y un solo editor (relación uno a muchos – clave ajena – con los editores).

El primer paso del uso de este diseño de base de datos con Django es expresarlo como código Python. En Tmodels.pyT, el archivo que fue creado por el comando startapp, escriba lo siguiente:

from django.db import models

class Publisher(models.Model):

Page 40: Tutorial de Django

name = models.CharField(max_length=30) address = models.CharField(max_length=50) city = models.CharField(max_length=60) state_province = models.CharField(max_length=30) country = models.CharField(max_length=50) website = models.URLField()

class Author(models.Model):

first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField()

class Book(models.Model):

title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()

La primera cosa a notar es que cada modelo está representado por una clase Python que es una Tsubclase de django.db.models.ModelT. La clase padre, Model, contiene toda la maquinaria necesaria para hacer que estos objetos sean capaces de interactuar con una base de datos. Lo crea o no, esto es todo el código que tiene que escribir para tener acceso básico a los datos con Django.

Cada modelo corresponde generalmente a una tabla de base de datos única, y cada atributo del modelo generalmente corresponde a una columna de esa tabla de base de datos. El nombre del atributo corresponde al nombre de la columna, y el tipo de campo (por ejemplo, CharField) corresponde al tipo de la columna de base de datos (por ejemplo, varchar).

Django puede generar el comando CREATE TABLE automáticamente, como mostraremos después.

La excepción a la regla de una clase por cada tabla de base de datos es el caso de las relaciones muchos-a-muchos. En nuestro modelo de ejemplo, el libro tiene un campo TManyToMany T llamado autores. Esto designa que un libro tiene uno o varios autores, pero la tabla de base de datos Libros no tiene una columna de autores. En lugar de ello, Django crea una tabla adicional, una tabla de referencias cruzadas de muchos-a-muchos , que se encarga del mapeo de los libros a los autores.

Por último, tenga en cuenta que no hemos definido explícitamente una clave primaria en ninguno de estos modelos. A menos que usted lo indique, Django automáticamente le da a cada modelo una clave primaria de entero autonumérico llamado id. TCada modelo de Django necesita tener una clave primaria de una sola columnaT.

Instalar el modelo

Page 41: Tutorial de Django

Ahora vamos a crear las tablas en nuestra base de datos. Con el fin de hacer eso, el primer paso es activar estos modelos en nuestro proyecto Django. Lo hacemos mediante la Tadición de la aplicación TTbooks TT a la lista de “aplicaciones instaladas” en el archivo de configuraciónT.

Edite el archivo Tsettings.py T de nuevo, y busque la propiedad INSTALLED_APPS, que le dice a Django que aplicaciones están activas para un proyecto determinado. De forma predeterminada, se ve algo como esto:

INSTALLED_APPS = (

‘django.contrib.auth’, ‘django.contrib.contenttypes’, ‘django.contrib.sessions’, ‘django.contrib.sites’,

)

Temporalmente comentemos las cuatro líneas poniendo una almohadilla (#) al principio de ellos. Comentenmos también la propiedad por defecto MIDDLEWARE_CLASSES. A continuación, añadir: Tmysite.books T a la lista INSTALLED_APPS, por lo que la propiedad finalmente podría parecerse a esto:

MIDDLEWARE_CLASSES = (

# ‘django.middleware.common.CommonMiddleware’, # ‘django.contrib.sessions.middleware.SessionMiddleware’, # ‘django.contrib.auth.middleware.AuthenticationMiddleware’,

)

INSTALLED_APPS = (

# ‘django.contrib.auth’, # ‘django.contrib.contenttypes’, # ‘django.contrib.sessions’, # ‘django.contrib.sites’, ‘mysite.books’,

)

Ahora que la aplicación Django se ha activado en el archivo de configuración, podemos crear las tablas de base de datos en nuestra base de datos. En primer lugar, vamos a Tvalidar los modelosT con la ejecución de este comando:

python manage.py validate

Si sus modelos son válidos, ejecute el comando siguiente para que Django genere las declaraciones CREATE TABLE de los modelos de la aplicación books:

Page 42: Tutorial de Django

python manage.py sqlall books

El comando TsqlallT en realidad no crea las tablas en su base de datos – sólo imprime en pantalla la salida para que pueda ver el SQL que Django ejecutaría. Si quisiera, podría copiar y pegar este SQL en su cliente de base de datos. Sin embargo, Django proporciona una manera más fácil de llevar a cabo las sentencias SQL en la base de datos: el comando Tsyncdb T:

python manage.py syncdb

Ejecutando ese comando verá algo como esto:

Creating table books_publisher Creating table books_author Creating table books_book Installing index for books.Book model

El comando TsyncdbT es una simple “sincronización” de sus modelos a su base de datos. Busca en todos los modelos de cada aplicación en su propiedad INSTALLED_APPS, comprueba la base de datos para ver si las tablas apropiadas existen ya, y crea las tablas si no existen aún. Tenga en cuenta que TsyncdbT no sincroniza los cambios en los modelos o las eliminaciones de los modelos; si usted hace un cambio a un modelo o elimina un modelo, y desea actualizar la base de datos, Tsyncdb T no gestionará eso.

Acceso básico a datos

Una vez que ha creado un modelo, Django proporciona automáticamente una API de alto nivel Python para trabajar con esos modelos. Pruébelo mediante la ejecución de Tpython manage.py shellT y escribiendo lo siguiente:

>>> from books.models import Publisher >>> p1 = Publisher(name=’Apress’, address=’2855 Telegraph Avenue’, … city=’Berkeley’, state_province=’CA’, country=’U.S.A.’, … website=’http://www.apress.com/’) >>> p1.save() >>> p2 = Publisher(name=”O’Reilly”, address=’10 Fawcett St.’, … city=’Cambridge’, state_province=’MA’, country=’U.S.A.’, … website=’http://www.oreilly.com/’) >>> p2.save() >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Publisher object>, <Publisher: Publisher object>]

Cuando crea objetos utilizando la API de modelos de Django, Django no guarda los objetos en la base de datos hasta que llama al Tmétodo save()T:

p1 = Publisher(…) # At this point, p1 is not saved to the database yet! p1.save() # Now it is.

Page 43: Tutorial de Django

Si desea crear un objeto y guardarlo en la base de datos en un solo paso, use el Tmétodo objects.create()T. Este ejemplo es equivalente al anterior:

>>> p1 = Publisher.objects.create(name=’Apress’, … address=’2855 Telegraph Avenue’, … city=’Berkeley’, state_province=’CA’, country=’U.S.A.’, … website=’http://www.apress.com/’) >>> p2 = Publisher.objects.create(name=”O’Reilly”, … address=’10 Fawcett St.’, city=’Cambridge’, … state_province=’MA’, country=’U.S.A.’, … website=’http://www.oreilly.com/’) >>> publisher_list = Publisher.objects.all() >>> publisher_list

Añadir representaciones de cadena del Modelo

Cuando mostramos la lista de editores, todo lo que obtuvimos en pantalla fue una vista inútil que hace difícil distinguir los objetos Publisher:

[<Publisher: Publisher object>, <Publisher: Publisher object>]

Podemos solucionar este problema fácilmente mediante la adición de un TmétodoT llamado T__unicode__()T a nuestra clase Publisher. Un método __unicode__() le dice a Python cómo mostrar la representación “Unicode” de un objeto.

Puede ver esto en acción añadiendo un __unicode__() para los tres modelos:

from django.db import models

class Publisher(models.Model):

name = models.CharField(max_length=30)

address = models.CharField(max_length=50)

city = models.CharField(max_length=60)

state_province = models.CharField(max_length=30)

country = models.CharField(max_length=50)

website = models.URLField()

def __unicode__(self):

return self.name

class Author(models.Model):

first_name = models.CharField(max_length=30)

Page 44: Tutorial de Django

last_name = models.CharField(max_length=40)

email = models.EmailField()

def __unicode__(self):

return u’%s %s’ % (self.first_name, self.last_name)

class Book(models.Model):

title = models.CharField(max_length=100)

authors = models.ManyToManyField(Author)

publisher = models.ForeignKey(Publisher)

publication_date = models.DateField()

def __unicode__(self):

return self.title

Para que los cambios __unicode__() surtan efecto, salga de la shell de Python y entre de nuevo. (Esta es la forma más sencilla de hacer cambios en el código actual.) Ahora la lista de objetos Publisher es mucho más fácil de entender:

>>> from books.models import Publisher >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Apress>, <Publisher: O'Reilly>]

Notar que __unicode__() es un buen ejemplo de añadir comportamiento a los modelos. Un modelo Django describe más que el diseño de la tabla de base de datos de un objeto, sino también cualquier funcionalidad de un objeto que sepa hacer. __unicode__() es un ejemplo de funcionalidad de este tipo; un modelo sabe cómo mostrarse a sí mismo.

Insertar y actualizar datos

Para insertar una fila en la base de datos, primero debe crear una instancia de su modelo con argumentos de clave, así:

>>> p = Publisher(name=’Apress’, … address=’2855 Telegraph Ave.’, … city=’Berkeley’, … state_province=’CA’, … country=’U.S.A.’, … website=’http://www.apress.com/’)

Page 45: Tutorial de Django

Este acto de crear instancias de un modelo de clase no afecta a la base de datos. El registro no se guarda en la base de datos hasta que llame a Tsave()T:

>>> p.save()

Debido a que el modelo utiliza un ID de clave primaria autonumérica, la llamada inicial a save() hace una cosa más: calcula el valor de clave principal para el registro y lo coloca en el atributo id de la instancia:

>>> p.id 52 # this will differ based on your own data

Las llamadas posteriores a save() van a guardar el registro, sin crear un nuevo registro (es decir, realizar una instrucción SQL UPDATE en lugar de un INSERT).

>>> p.name = ‘Apress Publishing’ >>> p.save()

Tenga en cuenta que todos los campos serán actualizados, y no sólo los que han sido cambiados.

Seleccionar objetos

Saber cómo crear y actualizar los registros de base de datos es esencial, pero es probable que las aplicaciones web que usted construya serán de hacer más consultas de los objetos existentes que de crear nuevos. Ya ha visto una manera de recuperar todos los registros para un determinado modelo:

>>> Publisher.objects.all() [<Publisher: Apress>, <Publisher: O'Reilly>]

Trasladado a SQL sería:

SELECT id, name, address, city, state_province, country, website FROM books_publisher;

Filtrar datos

Naturalmente, es raro que desee seleccionar todo de una base de datos a la vez; en la mayoría de los casos, usted querrá tratar con un subconjunto de los datos. En la API de Django, puede filtrar los datos utilizando el Tmétodo filter()T:

>>> Publisher.objects.filter(name=’Apress’) [<Publisher: Apress>]

filter() toma los argumentos que se traducen en las cláusulas where de SQL apropiadas. El ejemplo anterior se traduciría en algo como:

Page 46: Tutorial de Django

SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = ‘Apress’;

Se pueden pasar múltiples argumentos a filter() para reducir aún más las cosas:

>>> Publisher.objects.filter(country=”U.S.A.”, state_province=”CA”) [<Publisher: Apress>]

Los múltiples argumentos se traducen en cláusulas AND de SQL. Así, el ejemplo se traduce en:

SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = ‘U.S.A.’ AND state_province = ‘CA’;

Tenga en cuenta que por defecto las búsquedas utilizan el operador = de SQL para hacer búsquedas de concordancia exacta. Otros tipos de búsqueda están disponibles:

>>> Publisher.objects.filter(name__contains=”press”) [<Publisher: Apress>]

Hay un doble subrayado entre name y contains. Aquí, la parteT __contains T es traducida por Django en una sentencia LIKE de SQL:

SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE ‘%press%’;

Muchos otros tipos de búsquedas están disponibles, incluyendo icontains (LIKE insensible a mayúsculas/minúsculas), startswith y endswith, y range (consultas BETWEEN de SQL).

Recuperar objetos individuales

El ejemplos de filter() anteriores devolvían un TQuerySetT, que se puede tratar como una lista. A veces es más conveniente extraer solamente un objeto individual en lugar de una lista. Para eso está el Tmétodo get()T:

>>> Publisher.objects.get(name=”Apress”) <Publisher: Apress>

Una consulta que devuelva varios objetos producirá una excepción:

>>> Publisher.objects.get(country=”U.S.A.”) Traceback (most recent call last): … MultipleObjectsReturned: get() returned more than one Publisher – it returned 2! Lookup parameters were {‘country’: ‘U.S.A.’}

Page 47: Tutorial de Django

Una consulta que no devuelva objetos también causará una excepción:

>>> Publisher.objects.get(name=”Penguin”) Traceback (most recent call last): … DoesNotExist: Publisher matching query does not exist.

La excepción TDoesNotExistT es un atributo de la clase del modelo: Publisher.DoesNotExist. En sus aplicaciones, usted querrá atrapar estas excepciones, de esta forma:

try:

p = Publisher.objects.get(name=’Apress’)

except Publisher.DoesNotExist:

print “Apress isn’t in the database yet.”

else:

print “Apress is in the database.”

Ordenar datos

En los ejemplos anteriores, usted puede descubrir que los objetos se devuelven en un orden aparentemente al azar. Estamos simplemente devolviendo los datos en un orden arbitrario elegido por la base de datos. En sus aplicaciones Django, usted probablemente querrá pedir sus resultados de acuerdo a un cierto criterio – por ejemplo, por orden alfabético. Para ello, utilizar el método Torder_by() T:

>>> Publisher.objects.order_by(“name”) [<Publisher: Apress>, <Publisher: O'Reilly>]

Esto no parece muy diferente del all() del ejemplo anterior, pero el SQL ahora incluye un orden específico:

SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name;

Puede ordenar por el campo que quiera:

>>> Publisher.objects.order_by(“address”) [<Publisher: O'Reilly>, <Publisher: Apress>]

>>> Publisher.objects.order_by(“state_province”) [<Publisher: Apress>, <Publisher: O'Reilly>]

Para ordenar por múltiples campos usar varios argumentos:

Page 48: Tutorial de Django

>>> Publisher.objects.order_by(“state_province”, “address”) [<Publisher: Apress>, <Publisher: O'Reilly>]

También puede especificar orden inverso anteponiendo al nombre del campo un – (esto es un signo menos):

>>> Publisher.objects.order_by(“-name”) [<Publisher: O'Reilly>, <Publisher: Apress>]

Si bien esta flexibilidad es útil, utilizar order_by() todo el tiempo puede ser bastante repetitivo. La mayoría de las veces tendrá un campo particular por el querrá ordenar. En estos casos, Django permite especificar una ordenación predeterminada en el modelo:

class Publisher(models.Model):

name = models.CharField(max_length=30)

address = models.CharField(max_length=50)

city = models.CharField(max_length=60)

state_province = models.CharField(max_length=30)

country = models.CharField(max_length=50)

website = models.URLField()

def __unicode__(self):

return self.name

class Meta:

ordering = ['name']

En este caso, hemos introducido un nuevo concepto: la Tclase MetaT, que es una clase que está incrustada dentro de la definición de la clase Publisher. Usted puede utilizar la clase Meta en cualquier modelo para especificar opciones específicas del modelo.

Si se especifica esto, le dice a Django que a menos que se indique un ordenamiento de forma explícita con order_by(), todos los objetos Publisher deben ser ordenados por el campo name cada vez que se recuperen con la API de Base de Datos de Django.

Encadenar búsquedas

Hemos visto como filtrar datos y como ordenarlos. A menudo necesitaremos ambos. En estos casos, encadenar las búsquedas:

>>> Publisher.objects.filter(country=”U.S.A.”).order_by(“-name”) [<Publisher: O'Reilly>, <Publisher: Apress>]

Page 49: Tutorial de Django

Esto se convierte a una sentencia SQL con WHERE y ORDER BY:

SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE country = ‘U.S.A’ ORDER BY name DESC;

Trocear datos

Otra necesidad común es buscar sólo un número fijo de filas. Imagina que tiene miles de editores en su base de datos, pero que desea mostrar sólo el primero. Usted puede hacerlo con esta sintaxis:

>>> Publisher.objects.order_by(‘name’)[0] <Publisher: Apress>

Esto se traduce en:

SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name LIMIT 1;

De igual forma, usted puede recuperar un subconjunto específico de datos:

>>> Publisher.objects.order_by(‘name’)[0:2]

Esto devuelve 2 objetos, y se traduce en:

SELECT id, name, address, city, state_province, country, website FROM books_publisher ORDER BY name OFFSET 0 LIMIT 2;

Tener en cuenta que el índice negativo no está soportado:

>>> Publisher.objects.order_by(‘name’)[-1] Traceback (most recent call last): … AssertionError: Negative indexing is not supported.

Esto es fácil de conseguir, sin embargo. Sólo cambiar la sentencia order_by() de esta forma:

>>> Publisher.objects.order_by(‘-name’)[0]

Actualizar varios objetos en una sentencia

Page 50: Tutorial de Django

Hemos señalado en la sección “Inserción y actualización de datos” que el método save() del modelo actualiza todas las columnas de una fila. Dependiendo de su aplicación, es posible que desee actualizar sólo un subconjunto de columnas.

Por ejemplo, supongamos que usted desea actualizar la editorial Apress para cambiar el nombre de ‘Apress’ a ‘Apress Publishing’. Usando save(), se vería algo como esto:

>>> p = Publisher.objects.get(name=’Apress’) >>> p.name = ‘Apress Publishing’ >>> p.save()

Esto se traduce en el siguiente SQL:

SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = ‘Apress’;

UPDATE books_publisher SET

name = ‘Apress Publishing’,

address = ’2855 Telegraph Ave.’,

city = ‘Berkeley’,

state_province = ‘CA’,

country = ‘U.S.A.’,

website = ‘http://www.apress.com’

WHERE id = 52;

El método save() de Django establece todos los valores de las columnas, no sólo la columna name. Si está en un entorno en el que otras columnas de la base de datos podrían cambiar a causa de algún otro proceso, es más inteligente cambiar sólo la columna que usted necesita. Para ello, utilice el método update() en objetos QuerySet. He aquí un ejemplo:

>>> Publisher.objects.filter(id=52).update(name=’Apress Publishing’)

La traducción a SQL es mucho más eficiente y no provoca efectos laterales:

UPDATE books_publisher SET name = ‘Apress Publishing’ WHERE id = 52;

El método update() funciona en cualquier TQuerySetT, lo que significa que usted puede editar varios registros de forma masiva. He aquí cómo usted puede cambiar el país de ‘U.S.A.’ a ‘USA’ en todos los registros Publisher:

Page 51: Tutorial de Django

>>> Publisher.objects.all().update(country=’USA’) 2

El método update() devuelve un valor: un entero que representa cuántos registros han cambiado. En el ejemplo anterior, fue 2.

Eliminar objetos

Para eliminar un objeto de la base de datos, simplemente llamar al Tmétodo delete()T:

>>> p = Publisher.objects.get(name=”O’Reilly”) >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>]

También puede eliminar objetos de forma masiva llamando al método delete() del resultado de cualquier QuerySet. Esto es similar al método update() que vimos en la última sección:

>>> Publisher.objects.filter(country=’USA’).delete() >>> Publisher.objects.all().delete() >>> Publisher.objects.all() []

Tenga cuidado al borrar sus datos. Como medida de precaución contra la supresión de todos los datos de una tabla particular, Django requiere que use explícitamente el método all() si desea borrar toda su tabla. Por ejemplo, esto no funciona:

>>> Publisher.objects.delete() Traceback (most recent call last): File ““, line 1, in AttributeError: ‘Manager’ object has no attribute ‘delete’

Pero funciona si le añade el Tmétodo all()T:

>>> Publisher.objects.all().delete()

Si está eliminando sólo un subconjunto de datos, no es necesario incluir all():

>>> Publisher.objects.filter(country=’USA’).delete()

HTUTutorial de Django – VUTH

12. mayo 2010 - Visitada 1765 veces, 1 hoy

HTUTécnicoUTH

Page 52: Tutorial de Django

TUEl sitio de administración de DjangoUT

Para una cierta clase de sitios Web, una interfaz de administración es una parte esencial de la infraestructura. Se trata de una interfaz basada en Web, limitada a los administradores del sitio de confianza, que permite la adición, la edición, y la eliminación de contenido del sitio.

Los paquetes django.contrib

La administración automática de Django es parte de una gran suite de funcionalidad de Django llamada django.contrib – la parte de código de Django que contiene diversas utilidades para el núcleo. Usted puede pensar en django.contrib como el equivalente de Django de la librería estándar de Python. Está integrado con Django de modo que no tenemos que reinventar la rueda en nuestras propias aplicaciones.

El sitio de administración se llama django.contrib.admin. Otras características disponibles en django.contrib incluyen un sistema de autenticación de usuarios (django.contrib.auth), soporte para sesiones anónimas (django.contrib.sessions), e incluso un sistema de comentarios de usuarios (django.contrib.comments).

Activar la interfaz de administración

El sitio de administración de Django es totalmente opcional, ya que sólo ciertos tipos de sitios necesitan esta funcionalidad. Eso significa que usted tendrá que dar unos pasos para activarlo en su proyecto.

En primer lugar, hacer unos cuantos cambios a su archivo de configuración:

1. Añadir ‘django.contrib.admin’ a la propiedad INSTALLED_APPS. 2. Asegúrese de que INSTALLED_APPS contiene ‘django.contrib.auth’,

‘django.contrib.contenttypes’, y ‘django.contrib.sessions’. El sitio de administración de Django requiere de estos tres paquetes.

3. Asegúrese de que MIDDLEWARE_CLASSES contiene los valores ‘django.middleware.common.CommonMiddleware’, ‘django.contrib.sessions.middleware.SessionMiddleware’, y ‘django.contrib.auth.middleware.AuthenticationMiddleware.

En segundo lugar, ejecutar Tpython manage.py syncdb T. Este paso permitirá instalar las tablas extra de base de datos que utiliza el interfaz de administración. La primera vez que ejecute syncdb con ‘django.contrib.auth’ en INSTALLED_APPS, se le preguntará sobre la creación de un super-usuario. Si usted no hace esto, tendrá que para ejecutar python manage.py createsuperuser por separado para crear una cuenta de usuario administrador, de lo contrario usted no será capaz de entrar en el sitio de administración.

Page 53: Tutorial de Django

En tercer lugar, agregue el sitio de administración a su URLconf (en Turls.py T, recuerde). Por defecto, el Turls.pyT generado por Tdjango-admin.py startprojectT contiene el código comentado para la administración de Django, y todo lo que tiene que hacer es quitar los comentarios.

# Include these import statements… from django.contrib import admin admin.autodiscover()

# And include this URLpattern… urlpatterns = patterns(”,

# … (r’^admin/’, include(admin.site.urls)), # …

)

Con esa poca configuración, ahora usted puede ver el sitio de administración de Django en acción. Simplemente ejecute el servidor de desarrollo (Tpython manage.py runserverT) y visite Thttp://127.0.0.1:8000/admin/ T en su navegador Web.

Usar el sitio de administración

Inicie sesión con el nombre de usuario y contraseña que agregó cuando creó el superusuario. Si usted no puede abrir una sesión, asegúrese de que ha creado realmente un super-usuario ejecutando Tpython manage.py createsuperuserT.

Una vez que esté conectado, lo primero que verá será la página de inicio de administración. Esta página muestra todos los tipos de datos disponibles que se pueden editar en el sitio de administración. En este punto, ya que no ha activado ninguno de nuestros modelos, la lista es escasa: sólo incluye Grupos y Usuarios, que son los dos modelos de administración por defecto editables.

Cada tipo de datos en el sitio de administración de Django tiene una lista de cambios y un formulario de edición. Las listas de cambios muestran todos los objetos disponibles en la base de datos, y los formularios de edición le permiten añadir, cambiar o eliminar registros particulares en su base de datos.

Haga clic en el vínculo Cambiar en la fila de los usuarios para cargar la página de lista de cambios de los usuarios. Esta página muestra todos los usuarios de la base de datos.

Pulse un nombre de usuario, y verá el formulario de edición de ese usuario. Esta página le permite cambiar los atributos del usuario.

Añadir modelos al sitio de administración

Vamos a añadir nuestros propios modelos a la página de administración a fin de que podemos agregar, modificar y eliminar objetos en nuestras tablas de bases de datos personalizadas utilizando esta interfaz agradable.

Page 54: Tutorial de Django

Vamos a seguir el ejemplo de los libros, donde definimos tres modelos: Publisher, Author, y Book.

En el directorio de libros (mysite/books), cree un archivo llamado Tadmin.py T, y escriba las siguientes líneas de código:

from django.contrib import admin from mysite.books.models import Publisher, Author, Book

admin.site.register(Publisher) admin.site.register(Author) admin.site.register(Book)

Este código le indica al sitio de administración de Django que ofrezca una interfaz para cada uno de estos modelos.

Una vez que haya hecho esto, vaya a la página de inicio de administración en su explorador Web ( Thttp://127.0.0.1:8000/admin/T). Usted debe ver una sección de Libros con enlaces a los autores, libros y editores. Tendrá que reiniciar runserver para que los cambios tengan efecto.

Un aspecto digno de mencionar aquí es el manejo de las claves ajenas y las relaciones muchos-a-muchos por el sitio de administración, las cuales aparecen en el modelo Book.

En la página Add Book del sitio de administración de Django (http://127.0.0.1:8000/admin/books/book/add/), el editor (una ForeignKey) es representado por una caja de selección, y el campo de los autores (un ManyToManyField) es representado por un cuadro de selección múltiple. Ambos campos están al lado de un signo más verde que te permite añadir los registros relacionados de ese tipo. Por ejemplo, si hace clic en el signo más verde junto al campo Publisher, obtendrá una ventana emergente que le permite añadir un editor.

Cómo trabaja el sitio de administración

Cuando Django carga su URLconf de Turls.pyT al arrancar el servidor, se ejecuta la setencia admin.autodiscover() que añadimos como parte de la activación de la administración. Esta función itera sobre la configuración de INSTALLED_APPS y busca un archivo llamado Tadmin.py T en cada aplicación instalada. Si un Tadmin.py T existe en una aplicación dada, se ejecuta el código de ese fichero.

En el Tadmin.py T de nuestra aplicación books, cada llamada a admin.site.register() simplemente registra el modelo dado con la administración. El sitio de administración mostrará una interfaz de edición/modificación para cada modelo que se haya registrado de forma explícita.

La aplicación django.contrib.auth incluye su propio Tadmin.py T, por lo que usuarios y grupos se mostraron de forma automática en la administración.

Page 55: Tutorial de Django

El sitio de administración de Django es sólo una aplicación Django, con sus propios modelos, plantillas, vistas, y patrones URL. Usted puede examinar sus plantillas, vistas, y patrones URL hurgando en django / contrib / admin en su copia del código base de Django.

Hacer campos opcionales

Usted notará probablemente una limitación – los formularios de edición requieren que se rellenen todos los campos, mientras que en muchos casos le gustaría que determinados campos fuesen opcionales. Digamos, por ejemplo, que queremos que nuestro campo email del modelo Author sea opcional, es decir, que se permita una cadena en blanco. En el mundo real, usted no podría tener una dirección de correo electrónico en archivo para cada autor.

Para especificar que el campo de correo electrónico es opcional, modificar el modelo Author. Basta con añadir Tblank = TrueT para el campo email, así:

class Author(models.Model):

first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True)

Al añadir blank = True, hemos comenzado la expansión de nuestro modelo más allá de una simple definición de la tabla de base de datos. Ahora, nuestra clase de modelo está empezando a convertirse en una rica colección de conocimiento sobre lo que los objetos Author son y qué pueden hacer. No sólo está el campo email representado por una columna VARCHAR en la base de datos, sino que es también un campo opcional en contextos como el sitio de administración de Django.

Hacer a fechas y campos numéricos opcionales

SQL tiene su propia forma de especificar los valores en blanco, un valor especial llamado NULL. NULL puede significar “desconocido” o “inválido”, o algún otro significado específico de la aplicación. En SQL, un valor de NULL es diferente de una cadena vacía, al igual que el objeto especial de Python None es diferente de la una cadena vacía (“”).

Esto puede causar confusión y ambigüedad no deseados: ¿Por qué este disco tiene un valor NULL, pero este otro tiene una cadena vacía? ¿Hay una diferencia, o se introdujeron los datos de manera distinta?. Y ¿Cómo puedo obtener todos los registros que tienen un valor en blanco, debería buscar tanto los registros NULL como las cadenas vacías, o debo elegir sólo los que tienen cadenas vacías?

Para evitar esa ambigüedad, Django genera automáticamente sentencias CREATE TABLE añadiendo un valor explícito NOT NULL a cada definición de columna.

En la mayoría de los casos, este comportamiento predeterminado es óptimo para su aplicación y le ahorrará dolores de cabeza por inconsistencia de los datos. Y funciona muy bien con el resto de Django, tales como el sitio de administración de Django, que

Page 56: Tutorial de Django

inserta una cadena vacía (no un valor NULL) cuando se deja un campo de carácter en blanco.

Pero hay una excepción con los tipos de columna de base de datos que no aceptan cadenas vacías como valores válidos, tales como fechas, horas y números. Si intenta insertar una cadena vacía en una columna de fecha o número entero, lo más probable es obtener un error de base la de datos. En este caso, NULL es la única manera de especificar un valor vacío. En los modelos Django, usted puede especificar que se permite NULL añadiendo null = True a un campo.

En resumen, si desea permitir valores en blanco en un campo de fecha (por ejemplo, DateField, TimeField, DateTimeField) o campo numérico (por ejemplo, IntegerField, DecimalField, FloatField), tendrá que utilizar tanto Tnull = True T como Tblank = TrueT.

Cambiemos nuestro modelo Book para permitir una fecha de publicación en blanco:

class Book(models.Model):

title = models.CharField(max_length=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField(blank=True, null=True)

Agregar null = True es más complicado que añadir blank = True, porque null = True cambia la semántica de la base de datos, es decir, cambia la sentencia CREATE TABLE para eliminar el NOT NULL del campo publication_date. Para completar este cambio, Tnecesitaremos actualizar la base de datosT.

Por varias razones, Django no trata de automatizar los cambios en los esquemas de base de datos, por lo que es su propia responsabilidad ejecutar el ALTER TABLE adecuado siempre que se realice un cambio a un modelo. Recuerde que puede utilizar python manage.py dbshell para entrar en la shell de su servidor de base de datos. He aquí cómo quitar el NOT NULL en este caso en particular:

ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL;

Ahora el formulario de edición Add Book debería permitir valores de fecha de publicación vacíos.

Personalizar etiquetas de campo

En los formularios de edición del sitio de administración, la etiqueta de cada campo se genera a partir del nombre de campo del modelo. El algoritmo es simple: Django simplemente reemplaza los guiones bajos con espacios y coloca en mayúsculas el primer carácter. Así, por ejemplo, el campo publication_date del modelo Book tiene la fecha Publicación Date.

Sin embargo, los nombres de campo no siempre se prestan a etiquetas de campo agradables en administración, así que en algunos casos es posible que desee personalizar una etiqueta. Usted puede hacer esto especificando Tverbose_nameT en el

Page 57: Tutorial de Django

campo del modelo adecuado. Por ejemplo, así es cómo podemos cambiar la etiqueta del campo Author.email a “e-mail”, con guión:

class Author(models.Model):

first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True, verbose_name=’e-mail’)

Por último, tenga en cuenta que puede pasar el verbose_name como un argumento de posición, para una sintaxis más compacta. Este ejemplo es equivalente al anterior:

class Author(models.Model):

first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(‘e-mail’, blank=True)

Esto no funcionará con campos TManyToManyFieldT o TForeignKeyT, debido a que requieren que el primer argumento sea una clase modelo. En esos casos, especificar verbose_name explícitamente.

Personalizar clases ModelAdmin

Los cambios que hemos hecho hasta ahora – blank = True, null = True, y verbose_name- son realmente cambios de nivel de modelo, no cambios de nivel de administración. Es decir, estos cambios son, fundamentalmente, una parte del modelo y sólo para ser utilizados por el sitio de administración, no hay nada específico de administración en ellos.

Más allá de estos, el sitio de administración de Django ofrece una gran cantidad de opciones que permiten personalizar la forma en que el sitio de administración trabaja para un modelo en particular. Tales opciones residen en clases TModelAdminT, que son clases que contienen la configuración de un modelo específico en una instancia del sitio de administración específica.

Personalizar listas de cambios

Entremos en la personalización de administración, especificando los campos que se muestran en la lista de cambios para el modelo Author. De forma predeterminada, la lista de cambios muestra el resultado de T__unicode__()T para cada objeto.

Podemos mejorar este comportamiento predeterminado, añadiendo algunos campos más a la muestra de la lista de cambios. Sería útil, por ejemplo, ver cada e-mail del autor en esta lista, y sería bueno poder ordenarla por nombre y apellido.

Para que esto ocurra, vamos a definir una clase TModelAdmin T para el modelo de Autor. Esta clase es la clave para personalizar la administración, y una de las cosas más básicas que le permite hacer es especificar la lista de campos a mostrar en las páginas de lista de cambios. Editar Tadmin.py T para realizar estos cambios:

Page 58: Tutorial de Django

from django.contrib import admin from mysite.books.models import Publisher, Author, Book

class AuthorAdmin(admin.ModelAdmin):

list_display = (‘first_name’, ‘last_name’, ‘email’)

admin.site.register(Publisher) admin.site.register(Author, AuthorAdmin) admin.site.register(Book)

Volver a cargar la página de lista de cambios del autor, y verá que ahora se muestran tres columnas: el nombre, el apellido y la dirección de correo electrónico. Además, cada una de esas columnas es ordenable haciendo clic en el encabezado de la columna.

Ahora agreguemos una barra de búsqueda simple. Añadir a Tsearch_fieldsT a AuthorAdmin, así:

class AuthorAdmin(admin.ModelAdmin):

list_display = (‘first_name’, ‘last_name’, ‘email’) search_fields = (‘first_name’, ‘last_name’)

Actualice la página en su navegador, y debería ver una barra de búsqueda en la parte superior. Hemos incluido una barra de búsqueda que busca en los campos first_name y last_name. Ahora agreguemos algunos filtros de fecha para nuestra página de lista de cambios del modelo Book:

from django.contrib import admin from mysite.books.models import Publisher, Author, Book

class AuthorAdmin(admin.ModelAdmin):

list_display = (‘first_name’, ‘last_name’, ‘email’) search_fields = (‘first_name’, ‘last_name’)

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,)

admin.site.register(Publisher) admin.site.register(Author, AuthorAdmin) admin.site.register(Book, BookAdmin)

Hemos utilizado Tlist_filterT, que fija en una tupla los campos a utilizar para crear filtros en el lado derecho de la página de lista de cambios. Para los campos fecha, Django proporciona accesos directos para filtrar la lista por “Hoy”, “Últimos 7 días”, “Este mes”, y “Este año”.

Page 59: Tutorial de Django

list_filter también trabaja en campos de otros tipos, no sólo DateField. (Pruebe con los campos BooleanField y ForeignKey, por ejemplo.) Los filtros se muestran siempre que haya por lo menos dos valores a elegir.

Otra manera de ofrecer filtros de fecha es utilizar la opción de administración Tdate_hierarchy T, así:

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,) date_hierarchy = ‘publication_date’

Tenga en cuenta que date_hierarchy toma una cadena, no una tupla, porque sólo un campo de fecha puede utilizarse para la jerarquía.

Por último, vamos a cambiar el orden por defecto en que se muestran los libros en la página de lista de cambios a siempre ordenado descendente por la fecha de su publicación.

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,) date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,)

Usando estas opciones, usted puede hacer una interfaz de edición de datos muy potente, lista para producción, con sólo unas pocas líneas de código.

Personalizar los formularios de edición

En primer lugar, vamos a personalizar la manera en que se ordenan los campos. De forma predeterminada, el orden de los campos en un formulario de edición corresponde al orden en que están definidas en el modelo. Nosotros podemos cambiar eso utilizando la opción Tfields T en nuestra subclase TModelAdmin T:

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,) date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,) fields = (‘title’, ‘authors’, ‘publisher’, ‘publication_date’)

Otra cosa útil de la opción fields es que permite excluir a ciertos campos de la edición. Sólo dejando fuera los campo(s) que desea excluir. Por ejemplo, en nuestra base de datos de libros, se podría impedir que el campo publication_date fuese editable:

class BookAdmin(admin.ModelAdmin):

Page 60: Tutorial de Django

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,) date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,) fields = (‘title’, ‘authors’, ‘publisher’)

Otro uso común de personalizar los formularios de edición tiene que ver con los campos muchos a muchos. El sitio de administración representa cada TManyToManyFieldT como una caja de selección múltiple. Si desea seleccionar varios elementos, hay que mantener pulsada la tecla Control. El sitio de administración amablemente inserta un fragmento de texto que explica esto, pero, aún así, se hace difícil de manejar cuando su campo contiene cientos de opciones. La solución del sitio de administración es Tfilter_horizontalT.

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,) date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,) filter_horizontal = (‘authors’,)

Recomendamos el uso filter_horizontal para cualquier ManyToManyField que tenga más de diez elementos. Es mucho más fácil de utilizar.También, recuerde que puede usar filter_horizontal en múltiples campos, especificando solamente cada nombre en la tupla. Las clases TModelAdminT también tienen una opción Tfilter_verticalT. Funciona exactamente como filter_horizontal, pero la interfaz coloca las dos casillas verticalmente en vez de horizontalmente. Es una cuestión de gusto personal.

filter_horizontal y filter_vertical funcionan sólo en los campos TManyToManyFieldT, no en campos TForeignKeyT. Por defecto, el sitio de administración usa <select> simples para los campos TForeignKeyT, pero, como para TManyToManyFieldT, a veces usted no quiere incurrir en la sobrecarga de tener que seleccionar todos los objetos relacionados con la pantalla en el menú desplegable. Por ejemplo, si nuestra base de datos de libros crece para incluir a miles de editores, el formulario Add Book podría tardar bastante en cargar, ya que tiene que cargar cada editor a mostrar en el cuadro <select>.

Puedes solucionar este problema con una opción llamada Traw_id_fieldsT. Ponga esto en una tupla de nombres de campo TForeignKeyT, y los campos se mostrarán en el administrador con una caja de entrada de texto simple (<input type = “text”>) en lugar de un <select>.

class BookAdmin(admin.ModelAdmin):

list_display = (‘title’, ‘publisher’, ‘publication_date’) list_filter = (‘publication_date’,) date_hierarchy = ‘publication_date’ ordering = (‘-publication_date’,) filter_horizontal = (‘authors’,) raw_id_fields = (‘publisher’,)

Page 61: Tutorial de Django

¿Qué introduciría en este cuadro de entrada? El identificador de la base de datos del editor.Teniendo en cuenta que los seres humanos normalmente no memorizan identificadores base de datos, hay un icono en el que puede hacer clic para iniciar una ventana desde la que puede seleccionar el editor.

Usuarios, grupos y permisos

Ya que está conectado como super-usuario, tiene acceso a crear, editar y eliminar cualquier objeto. Naturalmente, los diferentes entornos requieren de sistemas de autorización diferentes, no todos pueden o deben ser un superusuario. El sitio de administración de Django utiliza un sistema de permisos que se puede utilizar para dar acceso específico a los usuarios a las partes de la interfaz que necesitan.

Estas cuentas de usuario están destinadas a ser lo suficientemente generales como para utilizarse fuera de la interfaz de administración, pero las trataremos como cuentas de usuario de administración por ahora.

Puede editar los usuarios y los permisos a través de la interfaz de administración al igual que cualquier otro objeto. Los objetos de usuario tiene los campos nombre de usuario estándar, contraseña, e-mail, y nombre real, junto con un conjunto de campos que definen lo que el usuario puede hacer en la interfaz de administración. En primer lugar, hay un conjunto de 3 banderas boolean:

• El flag “activo” controla si el usuario está activo. • El flag “staff” controla si el usuario tiene permiso para acceder a la interfaz de

administración. • El flag “superusuario” proporciona al usuario acceso completo a agregar, crear y

eliminar cualquier elemento de la interfaz de administración.

Los usuarios de administración “normales” – es decir, los miembros activos, no superusuarios – se les concede acceso de administración a través de los permisos asignados. Cada objeto editable a través de la interfaz de administración (por ejemplo, libros, autores, editores) tiene tres permisos: crear, editar y eliminar. La asignación de permisos a un usuario concede al usuario el correspondiente nivel de acceso.

Al crear un usuario, dicho usuario no tiene permisos. Por ejemplo, usted puede dar a un usuario permiso para agregar y cambiar los editores, pero no para eliminarlos. Tenga en cuenta que estos permisos se definen por el modelo, no por objeto, de forma que usted diga, “Juan puede hacer cambios en cualquier libro,” pero no le permite decir, “Juan puede hacer los cambios de cualquier libro publicado por Apress. ”

También puede asignar usuarios a grupos. Un grupo es simplemente un conjunto de permisos que se aplican a todos los miembros de ese grupo. Los grupos son útiles para la concesión de permisos idénticos a un subconjunto de usuarios.

HTUTutorial de Django – VI UTH

Page 62: Tutorial de Django

16. julio 2010 - Visitada 1692 veces, 2 hoy

HTUTécnicoUTH

TUFormularios UT

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.

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

Page 63: Tutorial de Django

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))

Page 64: Tutorial de Django

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 Tbooks/views.pyT.

La plantilla de acompañamiento, Tsearch_form.html T, 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 Turls.py T podría ser algo como esto:

Page 65: Tutorial de Django

urlpatterns = patterns(”,

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

)

Ahora, si ejecuta el runserver y visita Thttp://127.0.0.1:8000/search-form/T, 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

Page 66: Tutorial de Django

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 Tviews.py T):

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 Tsearch_results.htmlT 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 %}

Page 67: Tutorial de Django

</ul>

{% else %}

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

{% endif %}

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>

Page 68: Tutorial de Django

</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})

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.

Page 69: Tutorial de Django

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 Tsearch_form.html T 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})

Page 70: Tutorial de Django

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 Tsearch_form.htmlT 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>

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:

Page 71: Tutorial de Django

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 Tsearch_form.html T 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.

Page 72: Tutorial de Django

Empezaremos con nuestra plantilla, Tcontact_form.html T:

<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’, ”):

Page 73: Tutorial de Django

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’, ‘[email protected]’), ['[email protected]'],

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

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

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.

Page 74: Tutorial de Django

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’, ‘[email protected]’), ['[email protected]'], )

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>

Page 75: Tutorial de Django

<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.

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

Page 76: Tutorial de Django

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 Tforms.py T. Cree este archivo en el mismo directorio que el Tviews.py T, 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>

Page 77: Tutorial de Django

&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’: ‘[email protected]’, … ‘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’: ”})

Page 78: Tutorial de Django

<<< 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’: ‘[email protected]’, … ‘message’: ‘Nice site!’}) <<< f.is_valid() True <<< f.cleaned_data {‘message’: u’Nice site!’, ‘e-mail’: u’[email protected]’, ‘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.

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):

Page 79: Tutorial de Django

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’, ‘[email protected]’), ['[email protected]'],

) 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”>

Page 80: Tutorial de Django

</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

Page 81: Tutorial de Django

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’, ‘[email protected]’), ['[email protected]'],

) 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)

Page 82: Tutorial de Django

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

Page 83: Tutorial de Django

Nuestra plantilla Tcontact_form.htmlT 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>

Page 84: Tutorial de Django

<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 %}”>

Page 85: Tutorial de Django

{% 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.

HTUTutorial de Django – VIIUTH

19. agosto 2010 - Visitada 1167 veces, 1 hoy

HTUTécnicoUTH

TUVistas y URLconfs avanzadas UT

Trucos URLconf

No hay nada “especial” en URLconfs – como todo en Django, sólo código Python.

Racionalizar las importaciones de funciones

Considerar la siguiente URLconf, basada en un ejemplo visto anteriormente:

Page 86: Tutorial de Django

from django.conf.urls.defaults import * from mysite.views import hello, current_datetime, hours_ahead

urlpatterns = patterns(”,

(r’^hello/$’, hello), (r’^time/$’, current_datetime), (r’^time/plus/(\d{1,2})/$’, hours_ahead),

)

Cada entrada en la URLconf incluye su vista asociada, pasada directamente como un objeto de función. Esto significa que es necesario importar la vista al prinicipio del módulo.

Pero, cuando una aplicación de Django crece en complejidad, su URLconf también crece, y gestionar esas importaciones puede ser tedioso. (Para cada nueva vista, usted tendría que recordar importarla, y la declaración de importación tiende a ser demasiado larga si se utiliza este enfoque.) Es posible evitar esto con la importación de propio módulo views. Este ejemplo de URLconf es equivalente al anterior:

from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^hello/$’, views.hello), (r’^time/$’, views.current_datetime), (r’^time/plus/(d{1,2})/$’, views.hours_ahead),

)

Django ofrece otra manera de especificar la vista de un patrón particular en el URLconf: se puede pasar una cadena que contenga el nombre del módulo y el nombre de la vista en lugar del objeto de función en sí mismo. Continuando con el ejemplo en curso:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^hello/$’, ‘mysite.views.hello’), (r’^time/$’, ‘mysite.views.current_datetime’), (r’^time/plus/(d{1,2})/$’, ‘mysite.views.hours_ahead’),

)

Notar las comillas que delimitan los nombres de las vistas.

Page 87: Tutorial de Django

Usando esta técnica, ya no es necesario importar las vistas; Django automáticamente importa la función de vista apropiada la primera vez que se necesita, según la cadena que describe el nombre y la ruta de la función de vista.

Una abreviatura más que usted puede usar cuando utiliza esta técnica es factorizar un “prefijo de vista” común. En nuestro ejemplo de URLconf, cada una de las cadenas de vista comienza con ‘mysite.views’. Podemos factorizar este prefijo común y pasarlo como el primer argumento a los patrones, así:

from django.conf.urls.defaults import * urlpatterns = patterns(‘mysite.views’,

(r’^hello/$’, ‘hello’), (r’^time/$’, ‘current_datetime’), (r’^time/plus/(d{1,2})/$’, ‘hours_ahead’),

)

Con estos dos enfoques en mente, ¿cuál es mejor? Realmente depende de su estilo personal de programación y de sus necesidades.

Las ventajas de la cadena son las siguientes:

• Es más compacto, porque no es necesario importar las funciones de vista. • El resultado es URLconfs más legibles y manejables si las funciones de su vista

se reparten entre diferentes módulos de Python.

Las ventajas del enfoque de objeto de función son las siguientes:

• Se permite un fácil “ajuste” de las funciones de vista. • Es más acorde con las tradiciones de Python, como pasar funciones como

objetos.

Ambos enfoques son válidos, e incluso se pueden mezclar en el mismo URLconf. La elección es suya.

Usar varios Prefijos de Vista

En la práctica, si utiliza la técnica de cadena, probablemente acabe mezclando vistas que no tienen un prefijo común. Sin embargo, usted aún puede usar la abreviatura del prefijo de vistas para eliminar la duplicación. Sólo tiene que añadir varios objetos patterns() juntos, así:

from django.conf.urls.defaults import *

urlpatterns = patterns(‘mysite.views’,

(r’^hello/$’, ‘hello’), (r’^time/$’, ‘current_datetime’), (r’^time/plus/(\d{1,2})/$’, ‘hours_ahead’),

Page 88: Tutorial de Django

)

urlpatterns += patterns(‘weblog.views’,

(r’^tag/(\w+)/$’, ‘tag’),

)

URLs especiales en modo Debug

Hablando de la construcción de urlpatterns de forma dinámica, es posible que desee tomar ventaja de esta técnica alterando el comportamiento de su URLconf en el modo de depuración de Django. Para ello, basta con comprobar el valor de la propiedad DEBUG en tiempo de ejecución, así:

from django.conf import settings from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^$’, views.homepage), (r’^(\d{4})/([a-z]{3})/$’, views.archive_month),

)

if settings.DEBUG:

urlpatterns += patterns(”,

(r’^debuginfo/$’, views.debug),

)

En este ejemplo, la URL /debuginfo/ estará disponible sólo si la propiedad DEBUG es True.

Uso de Grupos con nombre

En todos nuestros ejemplos de URLconf hasta ahora, hemos usado, grupos de expresiones regulares sin nombre, es decir, ponemos entre paréntesis las partes de la URL que queremos capturar y, Django pasa ese texto capturado a la función de vista como un argumento posicional. En un uso más avanzado, es posible utilizar grupos con nombre de expresiones regulares para capturar los bits de la URL y pasarlos como argumentos de palabra clave a una vista.

En las expresiones regulares Python, la sintaxis para grupos con nombre de expresiones regulares es (?Ppattern), donde name es el nombre del grupo y pattern es un patrón de concordancia.

Page 89: Tutorial de Django

He aquí un ejemplo de URLconf que usa grupos sin nombre:

from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^articles/(\d{4})/$’, views.year_archive), (r’^articles/(\d{4})/(\d{2})/$’, views.month_archive),

)

Aquí, la misma URLConf reescrita, usando grupos con nombre:

from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^articles/(?P\d{4})/$’, views.year_archive), (r’^articles/(?P\d{4})/(?P\d{2})/$’, views.month_archive),

)

Esto logra exactamente lo mismo que el ejemplo anterior, con una sutil diferencia: los valores capturados se pasan a las funciones de vista como argumentos de palabras clave en lugar de argumentos posicionales.

Por ejemplo, con grupos sin nombre, una petición a /articles/2006/03/ daría lugar a una llamada a la función equivalente a esto:

month_archive(request, ’2006′, ’03′)

Con grupos con nombre, la misma petición resultaría en esta llamada a función:

month_archive(request, year=’2006′, month=’03′)

En la práctica, usar grupos con nombre hace a su URLconfs un poco más explícita y menos propensa a los errores del orden de los argumentos y puede volver a ordenar los argumentos de la función sus definiciones de vistas. Siguiendo el ejemplo anterior, si quisieramos cambiar la URL para incluir el mes antes del año, y estamos utilizando grupos sin nombre, tendríamos que recordar cambiar el orden de los argumentos en la vista month_archive. Si estuviéramos utilizando grupos con nombre, cambiar el orden de los parámetros capturados en la URL no tendría ningún efecto en la vista.

Por supuesto, los beneficios de los grupos con nombre llegan a costa de la brevedad; algunos desarrolladores encuentran la sintaxis de un grupo con nombre fea y demasiado detallada. Sin embargo, otra de las ventajas de los grupos con nombre es la legibilidad, especialmente para aquellos que no están íntimamente familiarizados con expresiones

Page 90: Tutorial de Django

regulares o su aplicación de Django particular. Es más fácil ver lo que sucede, de un vistazo, en una URLconf que utiliza grupos con nombre.

Comprender el algoritmo de agrupación/concordancia

Una advertencia con el uso de grupos con nombre en una URLconf es que un único patrón de URLconf no puede contener tanto grupos con nombre como sin nombre. Si usted hace esto, Django no dará ningún error, pero usted probablemente encontrará que sus URLs no son coincidentes tal y como usted espera. En concreto, aquí está el algoritmo que sigue el analizador URLconf:

• Si hay algunos argumentos con nombre, utilizará esos, haciendo caso omiso de los argumentos con nombre.

• De lo contrario, pasará todos los argumentos sin nombre como argumentos posicionales.

• En ambos casos, pasará opciones adicionales como argumentos de palabra clave.

Pasar opciones extra a funciones de vista

A veces usted se encontrará con la escritura de funciones de vista que son bastante similares, con sólo unas pequeñas diferencias. Por ejemplo, supongamos que tiene dos vistas cuyos contenidos son idénticos, excepto por las plantillas que utilizan:

# urls.py from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^foo/$’, views.foo_view), (r’^bar/$’, views.bar_view),

)

# views.py from django.shortcuts import render_to_response from mysite.models import MyModel

def foo_view(request):

m_list = MyModel.objects.filter(is_new=True) return render_to_response(‘template1.html’, {‘m_list’: m_list})

def bar_view(request):

m_list = MyModel.objects.filter(is_new=True) return render_to_response(‘template2.html’, {‘m_list’: m_list})

Estamos repitiendo el código, y eso es poco elegante. En principio, usted puede pensar eliminar la redundancia utilizando el mismo punto de vista, tanto para las direcciones

Page 91: Tutorial de Django

URL, poniendo entre paréntesis la URL a capturar, y comprobar la URL dentro de la vista para determinar la plantilla, así:

# urls.py from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^(foo)/$’, views.foobar_view), (r’^(bar)/$’, views.foobar_view),

)

# views.py from django.shortcuts import render_to_response from mysite.models import MyModel

def foobar_view(request, url):

m_list = MyModel.objects.filter(is_new=True) if url == ‘foo’:

template_name = ‘template1.html’

elif url == ‘bar’:

template_name = ‘template2.html’

return render_to_response(template_name, {‘m_list’: m_list})

El problema con esta solución, sin embargo, es que acopla las URL a su código. Si usted decide cambiar el nombre /foo/ a /fooey/, tendrá que acordarse de cambiar el código de la vista.

La solución elegante implica un parámetro URLconf opcional. Cada patrón en una URLconf puede incluir un tercer elemento: un diccionario de los argumentos de palabra clave para pasar a la función de vista.

Con esto en mente, podemos volver a escribir nuestro ejemplo actual de esta manera:

# urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns(”,

(r’^foo/$’, views.foobar_view, {‘template_name’: ‘template1.html’}), (r’^bar/$’, views.foobar_view, {‘template_name’: ‘template2.html’}),

)

Page 92: Tutorial de Django

# views.py from django.shortcuts import render_to_response from mysite.models import MyModel

def foobar_view(request, template_name):

m_list = MyModel.objects.filter(is_new=True) return render_to_response(template_name, {‘m_list’: m_list})

Esta técnica de opciones URLconf extra es una buena forma de enviar información adicional a las funciones de la vista con una sobrecarga mínimo.

Las siguientes secciones contienen un par de ideas sobre cómo utilizar la técnica de opciones de URLconf extra en sus propios proyectos.

Fingiendo valores URLConf capturados

Digamos que tiene un conjunto de vistas que coinciden con un patrón, junto con otra URL que no se ajusta a ese patrón, pero cuya lógica de la vista es la misma. En este caso, puede “falsear” la captación de valores URL mediante el uso de opciones URLconf extra para manejar esa URL adicional con la misma vista.

Por ejemplo, usted podría tener una aplicación que muestre algunos datos de un día en particular, con direcciones de Internet tales como las siguientes:

/mydata/jan/01/ /mydata/jan/02/ /mydata/jan/03/ # … /mydata/dec/30/ /mydata/dec/31/

Esto es bastante simple – usted puede capturarlos con una URLconf como esta (con sintaxis de grupos con nombre):

urlpatterns = patterns(”,

(r’^mydata/(?P\w{3})/(?P\d\d)/$’, views.my_view),

)

Y la función de vista sería así:

def my_view(request, month, day):

# ….

Ese método es sencillo – no es nada que no haya visto antes. El truco viene cuando se quiere añadir otra URL que utiliza my_view pero cuya URL no incluye un mes y/o día.

Page 93: Tutorial de Django

Por ejemplo, usted podría querer agregar otra URL, /mydata/birthday/, que sería equivalente a /mydata/jan/06/ :

urlpatterns = patterns(”,

(r’^mydata/birthday/$’, views.my_view, {‘month’: ‘jan’, ‘day’: ’06′}), (r’^mydata/(?P\w{3})/(?P\d\d)/$’, views.my_view),

)

Usted no tiene que cambiar su función de vista en absoluto.

Hacer una vista genérica

Es buena práctica de programación “factorizar” puntos comunes en el código. Por ejemplo, con estas dos funciones de Python:

def say_hello(person_name):

print ‘Hello, %s’ % person_name

def say_goodbye(person_name):

print ‘Goodbye, %s’ % person_name

podemos factorizarlas usando un parámetro:

def greet(person_name, greeting):

print ‘%s, %s’ % (greeting, person_name)

Podemos aplicar esta misma filosofía a las vista Django usando parámetros adicionales URLconf. Con esto en mente, usted puede comenzar a hacer abstracciones de alto nivel de sus vistas. En lugar de pensar: “Esta vista muestra una lista de objetos Event,” y “Esta vista muestra una lista de objetos BlogEntry”, dese cuenta que ambos son casos específicos de vista que muestra una lista de objetos, donde el tipo de objeto es variable.

Tome este código, por ejemplo:

# urls.py from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^events/$’, views.event_list), (r’^blog/entries/$’, views.entry_list),

)

Page 94: Tutorial de Django

# views.py from django.shortcuts import render_to_response from mysite.models import Event, BlogEntry

def event_list(request):

obj_list = Event.objects.all() return render_to_response(‘mysite/event_list.html’, {‘event_list’: obj_list})

def entry_list(request):

obj_list = BlogEntry.objects.all() return render_to_response(‘mysite/blogentry_list.html’,{‘entry_list’: obj_list})

Las dos vistas hacen esencialmente lo mismo: mostrar una lista de objetos. Factoricemos el tipo de objeto que estamos mostrando:

# urls.py from django.conf.urls.defaults import * from mysite import models, views

urlpatterns = patterns(”,

(r’^events/$’, views.object_list, {‘model’: models.Event}), (r’^blog/entries/$’, views.object_list, {‘model’: models.BlogEntry}),

)

# views.py from django.shortcuts import render_to_response

def object_list(request, model):

obj_list = model.objects.all() template_name = ‘mysite/%s_list.html’ % model.__name__.lower() return render_to_response(template_name, {‘object_list’: obj_list})

Con estos pequeños cambios, de repente tenemos una vista reutilizable, independiente del modelo. A partir de ahora, cada vez que necesitemos una vista que muestre un conjunto de objetos, podemos simplemente volver a utilizar esta vista object_list. Un par de apuntes de lo realizado:

• Pasamos las clases del modelo directamente, como el parámetro model. El diccionario de opciones URLConf extra puede pasar cualquier tipo de objeto – no sólo cadenas.

• Usamos model.__name__.lower () para determinar el nombre de la plantilla. Cada clase Python tiene un atributo __name__ que devuelve el nombre de la clase. Esta función es útil en momentos como éste, cuando no sabemos el tipo de clase hasta el tiempo de ejecución.

Page 95: Tutorial de Django

• Pasamos el nombre de variable genérico object_list a la plantilla. Fácilmente podríamos cambiar este nombre a blogentry_list o event_list.

Dado que los sitios web de bases de datos tienen varios patrones comunes, Django viene con un conjunto de “vistas genéricas” que utilizan esta técnica para ahorrar tiempo.

Dar opciones de configuración de vista

Si está desarrollando una aplicación Django, lo más probable es que los usuarios quieran algún grado de configuración. En este caso, es una buena idea añadir ganchos a sus vistas para las opciones de configuración que crea que la gente puede cambiar. Puede utilizar parámetros URLConf adicionales para este propósito.

Un parte común de una aplicación para configurar es el nombre de la plantilla:

def my_view(request, template_name):

var = do_something() return render_to_response(template_name, {‘var’: var})

Comprender la precedencia de los valores capturados frente a las opciones extra

Cuando hay un conflicto, los parámetros URLconf extra tienen prioridad sobre los parámetros de captura.

En otras palabras, si su URLconf captura una variable de un grupo con nombre y un parámetro URLconf extra incluye una variable con el mismo nombre, el valor del parámetro URLconf extra será utilizado. Por ejemplo, considere esta URLconf:

from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^mydata/(?P\d+)/$’, views.my_view, {‘id’: 3}),

)

Aquí, tanto la expresión regular como el diccionario extra incluyen un id. Cualquier petición (por ejemplo, /mydata/2/ o /mydata/432432/) será tratada como si id es 3, con independencia del valor capturado en la URL. Notamos esto sólo para ayudarle a evitar caer en el error.

Usar argumentos de vista por defecto

Otro truco conveniente es especificar los parámetros por defecto para los argumentos de una vista. Esto le dice a la vista que valor usar porn defecto para un parámetro si no se especifica ninguno. He aquí un ejemplo:

Page 96: Tutorial de Django

# urls.py from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^blog/$’, views.page), (r’^blog/page(?P\d+)/$’, views.page),

)

# views.py def page(request, num=’1′):

# Output the appropriate page of blog entries, according to num. # …

Aquí, ambas URLpatterns apuntan a la misma vista Tviews.pageT, pero el primer patrón no captura cualquier cosa desde la URL. Si el primer patrón concuerda, la función page() usará su argumento predeterminado para num, “1″. Si el segundo patrón concuerda, page() utilizará cualquier valor num capturado por la expresión regular.

Es común el uso de esta técnica en relación con las opciones de configuración, como se explicó anteriormente. En el siguiente ejemplo se hace una ligera mejora con el ejemplo de la sección “Dar opciones de configuración de vista”, proporcionando un valor predeterminado para Ttemplate_nameT:

def my_view(request, template_name=’mysite/my_view.html’):

var = do_something() return render_to_response(template_name, {‘var’: var})

Vistas de casos especiales

A veces usted tiene un patrón en su URLconf que maneja un gran conjunto de direcciones URL, pero usted necesitará un caso especial de ellas. En este caso, aprovechar la manera lineal en que trabaja una URLconf y colocar el caso particular en primer lugar.

Por ejemplo, usted puede pensar en páginas de “agregar un objeto” en el sitio de administración de Django, representado por un URLpattern así:

urlpatterns = patterns(”,

# … (‘^([^/]+)/([^/]+)/add/$’, views.add_stage), # …

)

Page 97: Tutorial de Django

Esto coincide con las URL como /myblog/entries/add/ y /auth/groups/add/. Sin embargo, la página “Añadir” para un objeto de usuario (/auth/user/add/) es un caso especial – que no muestra todos los campos del formulario, muestra dos campos de contraseña, etc. Podríamos resolver este problema mediante el caso especial en la vista, de este modo:

def add_stage(request, app_label, model_name):

if app_label == ‘auth’ and model_name == ‘user’:

# do special-case code

else:

# do normal code

pero eso es poco elegante por la razón que hemos visto varias veces en este capítulo: pone lógica de URL en la vista. Como una solución más elegante, podemos aprovechar el hecho de que las URLconfs se procesan en orden de arriba a abajo:

urlpatterns = patterns(”,

# … (‘^auth/user/add/$’, views.user_add_stage), (‘^([^/]+)/([^/]+)/add/$’, views.add_stage), # …

)

Ahora, una petición a /auth/user/add/ será manejada por la vista user_add_stage. A pesar de que la URL coincide con el segundo patrón, coincide con el de arriba primero.

Capturar texto en las URLs

Cada argumento capturado es enviado a la vista como una simple cadena Unicode de Python, independientemente del orden de concordancia que la expresión regular hace. Por ejemplo, en esta línea URLconf, el argumento year para views.year_archive() será una cadena, no un entero, incluso aunque \d{4} sólo concuerde con cadenas de enteros:

(r’^articles/(?P\d{4})/$’, views.year_archive),

Esto es importante tenerlo en cuenta cuando se está escribiendo el código de la vista. Muchas funciones Python sólo aceptann objetos de cierto tipo. Un error común es tratar de crear un objeto datetime.date con valores de cadena en lugar de valores enteros valores:

>>> import datetime >>> datetime.date(’1993′, ’7′, ’9′) Traceback (most recent call last):

Page 98: Tutorial de Django

… TypeError: an integer is required >>> datetime.date(1993, 7, 9) datetime.date(1993, 7, 9)

Trasladado a una URLConf y vista, el error se parecería a esto:

# urls.py from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

(r’^articles/(\d{4})/(\d{2})/(\d{2})/$’, views.day_archive),

)

# views.py import datetime

def day_archive(request, year, month, day):

# The following statement raises a TypeError! date = datetime.date(year, month, day)

En lugar de esto, day_archive() podría escribirse correctamente así:

def day_archive(request, year, month, day):

date = datetime.date(int(year), int(month), int(day))

Tenga en cuenta que int () eleva un ValueError al pasar una cadena que no está compuesto exclusivamente de dígitos, pero estamos evitando el error en este caso porque la expresión regular en nuestra URLconf asegura que sólo se pase cadenas que contienen dígitos a la función de vista.

Determinar contra qué busca la URLConf

Cuando una petición llega, Django intenta hacer coincidir los patrones de la URLConf contra la URL solicitada, como una cadena de Python. Esto no incluye los parámetros GET o POST, o el nombre de dominio. Tampoco incluye la barra principal, ya que cada dirección tiene una barra principal. Por ejemplo, en una solicitud de http://www.example.com/myapp/, Django tratará de satisfacer myapp/. En una solicitud de http://www.example.com/myapp/?page=3, Django tratará de satisfacer myapp/.

El método de petición (por ejemplo, POST, GET) no se tiene en cuenta a la hora de tratar con el URLconf. En otras palabras, todos los métodos de petición serán enviados a la misma función para la misma URL. Es la responsabilidad de una función de vista llevar a cabo la ramificación basada en el método de la solicitud.

Page 99: Tutorial de Django

Abstracciones de alto nivel de las funciones de vista

Hablando de ramificación basada en el método de la petición, vamos a echar un vistazo a cómo podríamos construir una buena manera de hacerlo. Considerar este diseño URLconf/Vista:

# urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns(”,

# … (r’^somepage/$’, views.some_page), # …

)

# views.py from django.http import Http404, HttpResponseRedirect from django.shortcuts import render_to_response def some_page(request):

if request.method == ‘POST’:

do_something_for_post() return HttpResponseRedirect(‘/someurl/’)

elif request.method == ‘GET’:

do_something_for_get() return render_to_response(‘page.html’)

else:

raise Http404()

En este ejemplo, el manejo de la vista some_page() de las peticiones POST y GET es muy diferente.

Lo único que tienen en común es una URL compartida URL: /somepage/. Como tal, es una falta de elegancia hacer frente tanto a POST y GET en la misma función de vista. Sería bueno si pudiéramos tener dos funciones de vista separadas – una que gestione las peticiones GET y otra que gestione las POST – y garantizar que cada una se invoque sólo cuando sea necesario.

Podemos hacer eso escribiendo una función de vista que delege a otras vistas, ya sea antes o después de ejecutar alguna lógica personalizada. He aquí un ejemplo de cómo esta técnica podría ayudar a simplificar nuestra vista some_page():

Page 100: Tutorial de Django

# views.py from django.http import Http404, HttpResponseRedirect from django.shortcuts import render_to_response

def method_splitter(request, GET=None, POST=None):

if request.method == ‘GET’ and GET is not None:

return GET(request)

elif request.method == ‘POST’ and POST is not None:

return POST(request)

raise Http404

def some_page_get(request):

assert request.method == ‘GET’ do_something_for_get() return render_to_response(‘page.html’)

def some_page_post(request):

assert request.method == ‘POST’ do_something_for_post() return HttpResponseRedirect(‘/someurl/’)

# urls.py from django.conf.urls.defaults import * from mysite import views

urlpatterns = patterns(”,

# … (r’^somepage/$’, views.method_splitter, {‘GET’: views.some_page_get, ‘POST’: views.some_page_post}), # …

)

Veamos que hace esto:

• Escribimos una nueva vista, method_splitter(), que delega a otras vistas según request.method. Busca dos argumentos de palabra clave, GET y POST, que deben ser funciones de vista. Si request.method es ‘GET’, llama a la vista GET. Si request.method es “POST”, llama a la vista POST. Si request.method es otra cosa (HEAD, etc), o si GET o POST no fueron suministrados a la función, se eleva un Http404.

Page 101: Tutorial de Django

• En el URLconf, apuntamos /somepage/ al method_splitter() y le pasarmos argumentos extra – las funciones para el uso de GET y POST, respectivamente.

• Por último, dividimos la vista some_page() vista en dos funciones de vista: some_page_get() y some_page_post (). Esto es mucho mejor que colocar toda la lógica en una sola vista.

Ahora tenemos una bonita función de vista genérica que encapsula la lógica de delegar una vista mediante request.method. Nada de method_splitter() está ligado a nuestra aplicación específica, por supuesto, asi que podemos reutilizarla en otros proyectos.

Pero hay una forma de mejorar el method_splitter(). Ahora, se asume que las vistas GET y POST no toman otros argumentos que request. ¿Qué pasa si se quiere utilizar method_splitter() con vistas que, por ejemplo, capturen texto de URLs o tomen argumentos de palabra clave opcionales?

Para ello, podemos usar una característica agradable de Python: argumentos variables con asteriscos. Vamos a mostrar el ejemplo primero y luego explicarlo:

def method_splitter(request, *args, **kwargs):

get_view = kwargs.pop(‘GET’, None) post_view = kwargs.pop(‘POST’, None) if request.method == ‘GET’ and get_view is not None:

return get_view(request, *args, **kwargs)

elif request.method == ‘POST’ and post_view is not None:

return post_view(request, *args, **kwargs)

raise Http404

Aquí, refactorizamos method_splitter() para eliminar los argumentos de palabra clave GET y POST en favor de *args y **kwargs. Esta es una característica Python que permite a una función aceptar un número arbitrario dinámico de argumentos cuyos nombres no se conocen hasta el tiempo de ejecución. Si coloca un asterisco delante de un parámetro en una definición de la función, cualquier argumento posicional a esa función se guarda en una sola tupla. Si coloca dos asteriscos delante de un parámetro en una definición de la función, cualquier argumento de palabras clave para esa función se guardará en un diccionario único.

Por ejemplo, tenga en cuenta esta función:

def foo(*args, **kwargs):

print “Positional arguments are:” print args print “Keyword arguments are:” print kwargs

Page 102: Tutorial de Django

Así es como funcionaría:

>>> foo(1, 2, 3) Positional arguments are: (1, 2, 3) Keyword arguments are: {} >>> foo(1, 2, name=’Adrian’, framework=’Django’) Positional arguments are: (1, 2) Keyword arguments are: {‘framework’: ‘Django’, ‘name’: ‘Adrian’}

Llevando esto a method_splitter(), se puede ver que estamos usando *args y **kwargs para aceptar argumentos a la función y distribuirlos a la vista apropiada. Pero antes de hacer eso, hacemos dos llamadas a kwargs.pop() para obtener los argumentos GET y POST, si están disponibles. (Estamos usando pop() con un valor por defecto None para evitar KeyError si alguno no está definido.)

Envolviendo funciones de vista

Nuestro truco de vista final se basa en una avanzada técnica de Python. Supongamos que se encuentre un montón de código repetitivo a través de diversas vistas, como en este ejemplo:

def my_view1(request):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

# … return render_to_response(‘template1.html’)

def my_view2(request):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

# … return render_to_response(‘template2.html’)

def my_view3(request):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

Page 103: Tutorial de Django

# … return render_to_response(‘template3.html’)

Aquí, cada vista comienza comprobando que request.user está autenticado, es decir, que el usuario se ha validado correctamente en el sitio web y sino lo redirige a /accounts/login/.

Sería bueno si pudiéramos eliminar ese trozo de código repetitivo de cada una de estas vistas y sólo marcarlos como que requieren autenticación. Podemos hacer esto haciendo un envoltorio de la vista. Tome un momento para estudiar esto:

def requires_login(view):

def new_view(request, *args, **kwargs):

if not request.user.is_authenticated():

return HttpResponseRedirect(‘/accounts/login/’)

return view(request, *args, **kwargs)

return new_view

Esta función, requires_login, toma una función de vista (view) y devuelve una nueva función de vista (new_view). La nueva función, new_view, se define dentro de requires_login y se encarga la lógica de controlar request.user.is_authenticated() y delegar a la vista original (view).

Ahora, podemos eliminar las comprobaciones if not request.user.is_authenticated() de nuestras vistas y simplemente envolverlas con requires_login en nuestra URLconf:

from django.conf.urls.defaults import * from mysite.views import requires_login, my_view1, my_view2, my_view3

urlpatterns = patterns(”,

(r’^view1/$’, requires_login(my_view1)), (r’^view2/$’, requires_login(my_view2)), (r’^view3/$’, requires_login(my_view3)),

)

Esto tiene el mismo efecto que antes, pero con menos código redundante. Ahora hemos creado una buena función genérica – requires_login() que nos puede envolver cualquier vista para requerir un inicio de sesión.

Incluir otras URLconfs

Si desea que su código sea usado en varios sitios basados en Django, debe considerar la organización de sus URLconfs de tal manera que se puedan “incluir”.

Page 104: Tutorial de Django

De esta forma, su URLconf puede “incluir” otros módulos URLconf. Por ejemplo, esta URLconf incluye otros URLconfs:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^weblog/’, include(‘mysite.blog.urls’)), (r’^photos/’, include(‘mysite.photos.urls’)), (r’^about/$’, ‘mysite.views.about’),

)

Hay un aspecto importante: las expresiones regulares en este ejemplo que apuntan a un include() no acaban en $, pero si una barra final. Cuando Django se encuentra con include(), corta parte de la URL concordante hasta ese punto y envía la cadena restante a la URLconf incluida para su posterior procesamiento.

Siguiendo este ejemplo, aquí está el URLconf mysite.blog.urls:

from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^(\d\d\d\d)/$’, ‘mysite.blog.views.year_detail’), (r’^(\d\d\d\d)/(\d\d)/$’, ‘mysite.blog.views.month_detail’),

)

Con estas dos URLconfs, aquí se muestra como se gestionarían algunas solicitudes:

• /weblog/2007/: En la primera URLconf, el patrón r’^weblog/’ coincide. Debido a que es un include(), Django divide todo el texto coincidente, que es ‘weblog/’ en este caso. La parte restante de la dirección es 2007/, que coincide con la primera línea en el URLconf mysite.blog.urls.

• /weblog//2007/ (con dos barras inclinadas): En la primera URLconf, el patrón r’^weblog/’ coincide. Debido a que es un include(), Django divide todo el texto coincidente, que es ‘weblog/’ en este caso. La parte restante de la dirección es /2007/, que no coincide con ninguna de las líneas en el URLconf mysite.blog.urls.

• /about/: Coincide con la vista mysite.views.about la primera URLconf, lo que demuestra que se pueden mezclar patrones include() con patrones no include.

Cómo trabajan con include() los parámetros capturados

Un URLconf incluido recibe cualquier parámetro capturado del URLconf padre, por ejemplo:

# root urls.py from django.conf.urls.defaults import *

Page 105: Tutorial de Django

urlpatterns = patterns(”,

(r’^(?P\w+)/blog/’, include(‘foo.urls.blog’)),

)

# foo/urls/blog.py from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^$’, ‘foo.views.blog_index’), (r’^archive/$’, ‘foo.views.blog_archive’),

)

En este ejemplo, la variable username capturada se pasa al URLconf incluido y, por lo tanto, a cada función de vista dentro de ese URLconf.

Tenga en cuenta que los parámetros capturados siempre serán pasados a todas las líneas del URLconf incluido, independientemente de si la vista de la línea en realidad acepta esos parámetros como válidos. Por esta razón, esta técnica es útil sólo si estás seguro de que todas las vistas de la URLconf incluida aceptan los parámetros que está pasando.

Cómo trabajan con include() las opciones URLconf extras

Del mismo modo, se pueden pasar opciones adicionales URLconf a include(), tal como se pueden pasar opciones URLconf adicionales para una vista normal – como un diccionario. Al hacer esto, a cada línea de la URLconf incluida se le pasan las opciones extra.

Por ejemplo, los siguientes conjuntos URLconf son funcionalmente idénticos.

Conjunto uno:

# urls.py from django.conf.urls.defaults import *

urlpatterns = patterns('',

(r’^blog/’, include(‘inner’), {‘blogid’: 3}),

)

# inner.py from django.conf.urls.defaults import *

urlpatterns = patterns(”,

Page 106: Tutorial de Django

(r’^archive/$’, ‘mysite.views.archive’), (r’^about/$’, ‘mysite.views.about’), (r’^rss/$’, ‘mysite.views.rss’),

)

Conjunto dos:

# urls.py from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^blog/’, include(‘inner’)),

)

# inner.py from django.conf.urls.defaults import *

urlpatterns = patterns(”,

(r’^archive/$’, ‘mysite.views.archive’, {‘blogid’: 3}), (r’^about/$’, ‘mysite.views.about’, {‘blogid’: 3}), (r’^rss/$’, ‘mysite.views.rss’, {‘blogid’: 3}),

)

Como es el caso de los parámetros capturados, las opciones adicionales siempre se pasarán a todas las líneas del URLconf incluido, independientemente de si la vista de la línea acepta esas opciones como válidas. Por esta razón, esta técnica es útil sólo si tiene la certeza de que todas las vistas incluidas en la URLconf aceptan las opciones extra que está pasando.