Tutorial de Django
Tutorial de Django
HTU UTH
Introduccin a Django
TU 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 mnimo de esfuerzo.
Instalar Python
T T
Django est escrito completamente en Python, por lo que el primer paso en la instalacin del marco es el asegurarse de que tiene Python instalado. http://www.python.org/download/
HTU UTH
Instalar Django
T T HTU
http://www.djangoproject.com/download/
UTH
Linux: sudo python setup.py install Windows: python setup.py install Los archivos de Django se instalarn en el directorio site-packages de su instalacin de Python, en donde Python busca las bibliotecas de terceros.
En una shell de comandos, cambie a otro directorio (no el directorio que contiene el directorio de Django) e inicie el intrprete de Python interactivo escribiendo python. Si la instalacin fue exitosa, usted debe ser capaz de importar el mdulo de Django: >>> import django >>> django.VERSION (1, 1, 0, final, 1)
En este punto, usted podra comenzar a escribir una aplicacin Web con Django, porque el nico requisito previo es una instalacin 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:
La configuracin 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 librera Python para nuestra base de datos de back-end. Esto es cdigo Python de terceros que permite interactuar con la base de datos.
Django requiere MySQL 4.0 o superior. Las versiones 3.x no soportan subconsultas anidadas y algunas otras sentencias SQL estndar . Tambin tendr que instalar el paquete de MySQLdb desde http://www.djangoproject.com/r/python-mysql/. En Linux, puede comprobar si su paquete de distribucin del sistema de gestin ofrece un paquete llamado python-mysql, python-MySQLdb, mysql-python, o algo similar.
HTU UTH
Iniciar un proyecto
T T
Una vez que haya instalado Python, Django, y (opcionalmente) el servidor/librera de base de datos, puede dar el primer paso del desarrollo de una aplicacin Django mediante la creacin de un proyecto. Un proyecto es una coleccin de ajustes de una instancia de Django, incluyendo la configuracin de la base de datos, las opciones especficas de Django, y la configuracin de la aplicacin. Si es la primera vez que usa Django, usted tendr que tener cuidado con la configuracin 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 mysite en el directorio actual.
T T
El comando startproject crea un directorio que contiene cuatro archivos: mysite/ __init__.py manage.py settings.py urls.py
__init__.py: Un archivo necesario para que Python trate el directorio mysite como un paquete (un grupo de mdulos de Python). Es un fichero vaco, y normalmente no se le aade nada. manage.py: Utilidad de lnea 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: Caractersticas de configuracin 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 vaco.
A pesar de su pequeo tamao, estos archivos ya constituyen una aplicacin Django de trabajo.
El servidor de desarrollo de Django (tambin 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 rpidamente, sin tener que lidiar con la configuracin del servidor de produccin (Apache, por ejemplo) hasta que est listo para la produccin. El servidor de desarrollo rastrea su cdigo y automticamente vuelve a cargarlo, por lo que es fcil para usted cambiar el cdigo 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 slo para las conexiones de su propio equipo. Ahora que est en ejecucin, visite http://127.0.0.1:8000/ con su navegador Web. Ver un Welcome to Django Funciona!
T T
Tutorial de Django II
HTU UTH
Vistas y URLconfs
TU UT
from django.http import HttpResponse def hello(request): return HttpResponse(Hello world) Una vista en Python es slo una funcin que toma un HttpRequest como su primer parmetro y devuelve una instancia de HttpResponse. Para que una funcin de Python sea una vista Django, tiene que hacer esas dos cosas. (Hay excepciones, pero las veremos ms tarde.) Si en este momento ejecuta python manage.py runserver de nuevo, seguirs viendo el mensaje Bienvenido a Django, sin ningn rastro de Hola mundo. Eso se debe a que el proyecto mysite no conoce la vista hello; se necesita ahora decirle a Django explcitamente 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. Bsicamente, es un mapeo entre las URL y las funciones de vista que deben llamarse para esas direcciones URL. El URLconf por defecto incluye algunas caractersticas comentadas de uso comn en Django, por lo que la activacin de esas caractersticas es tan fcil como descomentar las lneas adecuadas. Si se ignora el cdigo comentado, aqu est la esencia de un URLconf:
T T T T T T
Lo ms importante a destacar es la variable urlpatterns, que Django espera encontrarla en el mdulo URLconf. Esta variable define la asignacin entre las direcciones URL y el cdigo que controla las direcciones URL. Por defecto, el URLconf est vaco la aplicacin Django est en blanco. Para agregar una URL y una vista al URLconf, simplemente aadir una tupla Python que asigna un patrn de URL a la funcin de la vista. He aqu cmo 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 peticin a la URL /hello/ debera ser gestionada por la funcin 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 ejecutndose tambin est bien. El servidor de desarrollo detecta automticamente los cambios en su cdigo 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 direccin 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 pgina web en Django.
from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now()
html = It is now %s. % now return HttpResponse(html) Al igual que con la funcin de vista hello, esta debe residir en views.py. Este es el aspecto completo de views.py:
T T T 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) Despus de aadir esto a views.py, agreguar el patrn URL a urls.py para decirle a Django que URL debe manejar esta vista. Algo como /time/ tendra sentido:
T T
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 debera ver la fecha y hora actuales.
llaman estn dbilmente acopladas, es decir, la decisin de lo que la URL debe ser para una determinada funcin y la implementacin de la funcin 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 direccin por ejemplo, para moverla de /time/ a /current-time/ podra hacer un cambio rpido en URLconf sin tener que preocuparse por la vista. Del mismo modo, si usted quisiera cambiar la funcin de vista que altera la lgica de alguna manera, podra hacerlo sin afectar a la URL a la que est vinculada la funcin. Adems, si usted quiere exponer a la actual funcionalidad de la fecha en varias URL, usted fcilmente podra hacerlo editando URLconf, sin tener que tocar el cdigo de la vista. En este ejemplo, el current_datetime est disponible en dos URLs. Es un ejemplo artificial, pero esta tcnica puede ser til: urlpatterns = patterns(, (^hello/$, hello), (^time/$, current_datetime), (^another-time-page/$, current_datetime), ) Las URLconfs y las vistas estn dbilmente acopladas en la accin.
(^time/plus/2/$, two_hours_ahead), (^time/plus/3/$, three_hours_ahead), (^time/plus/4/$, four_hours_ahead), ) Entonces, cmo disear la aplicacin para manejar los desplazamiento de hora arbitrarios? La clave es usar comodines de patrones URL. Como se mencion anteriormente, un patrn URL es una expresin regular, por lo que puede utilizar el patrn de expresin regular \d+ para que coincida con uno o ms dgitos: urlpatterns = patterns(, # (r^time/plus/\d+/$, hours_ahead), # ) Este nuevo patrn 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 mximo de 99 horas. Esto significa que queremos permitir, nmeros de uno o de dos dgitos, y en la sintaxis de la expresin 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 carcter r al principio de la expresin 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 lnea. 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 minscula. Se recomienda fuertemente que utilice raw string en cualquier momento si est definiendo una expresin regular en Python. A partir de ahora, todos los urlpatterns en este libro sern raw string. Ahora que se ha designado un comodn para la direccin, usted necesita una manera de pasar esos datos de comodn a la funcin de vista, de modo que usted pueda utilizar una funcin de vista nica para cualquier desplazamiento de hora arbitrario. Usted puede hacer esto colocando entre parntesis los datos de la URLpattern que desea guardar. En el caso del ejemplo, lo que desea guardar es cualquier nmero que se introduzca en la URL, as que ponga parntesis alrededor de \d(1,2), de esta manera: (r^time/plus/(\d{1,2})/$, hours_ahead),
Usted est utilizando parntesis 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 nmero de horas de desplazamiento. Aqu est la vista de cdigo: 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 funcin de vista y el URLconf escrito, iniciar el servidor de desarrollo de Django (si no est ya en ejecucin), y visitar http://127.0.0.1:8000/time/plus/3/ para verificar que funciona. A continuacin, 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 patrn en el URLconf acepta nmeros slo de uno o dos dgitos; Django debera mostrar un error de Pgina no encontrada en este caso, tal y como vimos. La URL http://127.0.0.1:8000/time/plus/ (sin horas) tambin debera lanzar un error 404.
Plantillas Templates
TU UT
No es una buena idea codificar el HTML directamente en las vistas. Es mucho ms limpio y ms fcil de mantener separar el diseo de la pgina del cdigo Python. Podemos hacer esto con el sistema de plantillas de Django.
T T
You didnt order a warranty, so youre on your own when the products inevitably stop working. {% endif %} Sincerely, {{ company }} Esta plantilla es HTML bsico 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. Cmo podemos especificar los valores de las variables?Llegaremos a eso despus. Cualquier texto que est rodeado de llaves y porcentaje (por ejemplo, {% if ordered_warranted %}) es una etiqueta de plantilla. La definicin de una etiqueta es bastante amplia: una etiqueta slo le dice al sistema de plantillas haz algo. Por ltimo, el segundo prrafo de esta plantilla contiene un ejemplo de filtro, que es la forma ms 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, dndole 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 carcter de canalizacin (|). Cada plantilla Django tiene acceso a varias etiquetas y filtros incorporados, muchos de los cuales sern discutidos en las siguientes secciones.
Esta es la forma ms bsica en que se puede utilizar el sistema de plantillas de Django: 1. Crear un objeto Template proporcionando el cdigo de la plantilla raw como una cadena. 2. Llamar al mtodo 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.
Veamos algunos aspectos bsicos 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 cdigo de la plantilla raw internamente de forma optimizada, preparndolo para el renderizado. Pero si el cdigo de la plantilla incluye cualquier error de sintaxis, la llamada a Template() producir una excepcin TemplateSyntaxError.
T T
Renderizar un Template
Una vez que tiene un objeto Template, puede pasarle datos, dndole 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 Context, que reside en el mdulo django.template. Su constructor toma un argumento opcional: un diccionario que mapea los nombres de variables a los valores de las variables. Llamar al mtodo render() del objeto Template con el contexto para rellenar la plantilla:
T T
>>> from django.template import Context, Template >>> t = Template(My name is {{ name }}.) >>> c = Context({name: Stephane}) >>> t.render(c) uMy name is Stephane. Debemos sealar 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 captulo:
>>> from django.template import Template, Context >>> raw_template = " Dear {{ person_name }}, <p>Thanks for placing an order from {{ company }}. Its 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 didnt order a warranty, so youre 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. Its scheduled to\nship on April 2, 2009.</p>\n\n\n<p>You didnt order a warranty, so youre 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 mdulo 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 lneas, por contra, las cadenas entre comillas sencillas no pueden ser envueltas en varias lneas. 3. A continuacin creamos un objeto plantilla, t, pasando raw_template al constructor de la clase Template. 4. Importamos el mdulo de fecha y hora de la librera estndar de Python, porque la necesitaremos en la siguiente declaracin. 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 mtodo render () de nuestro objeto plantilla, pasndole 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.
Esos son los fundamentos del uso del sistema de plantillas de Django: simplemente escribir una cadena de plantilla, crear un objeto Template, crear un Context, y llamar al mtodo render().
T T
La clave para atravesar estructuras complejas de datos en las plantillas Django es el carcter de punto (.). Utilice un punto para acceder a las claves del diccionario, atributos, mtodos, o ndices de un objeto.
T T
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.)
>>> c = Context({person: person}) >>> t.render(c) uSally is 43 years old. Del mismo modo, los puntos tambin permiten el acceso a los atributos de los objetos. Por ejemplo, un objeto datetime.date de Python tiene atributos ao, mes, da, 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) uThe month is 5 and the year is 1993. Este ejemplo utiliza una clase personalizada, lo que demuestra que los puntos permiten tambin 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) uHello, John Smith. Los puntos tambin puede hacer referencia a mtodos de objetos. Por ejemplo, cada cadena Python tiene los mtodos 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})) uhelloHELLOFalse >>> t.render(Context({var: 123})) u123123True
Tenga en cuenta que no se incluyen los parntesis en las llamadas a mtodos. Adems, no es posible pasar argumentos a los mtodos, slo puede llamar a mtodos que no requieren argumentos. Por ltimo, los puntos tambin 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) uItem 2 is carrots. Las bsquedas de puntos se pueden resumir as: cuando la plantilla se encuentra con un punto en un nombre de variable, trata las bsquedas siguientes, en este orden:
Diccionario (por ejemplo, foo["bar"]) Atributo (por ejemplo, foo.bar) Llamada a mtodo (por ejemplo, foo.bar()) ndice de Lista (por ejemplo, foo[2])
El sistema utiliza el tipo de bsqueda que funciona primero. Es una falta de lgica del circuito. Las bsquedas del punto se pueden anidar varios niveles de profundidad. Por ejemplo, el ejemplo siguiente utiliza {{ person.name.upper() }}, que se traduce en una bsqueda de diccionario (person['name']) y luego una llamada al mtodo (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) uSALLY is 43 years old.
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})) uMy name is . Una llamada al mtodo slo funcionar si el mtodo no requiere argumentos. De lo contrario, el el sistema se mover al siguiente tipo de bsqueda (ndice de lista). Obviamente, algunos mtodos tienen efectos colaterales y sera 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 mtodo delete(). Si una plantilla incluye algo como {{ account.delete() }}, donde account es un objeto BankAccount, el objeto sera eliminado al procesar la plantilla. Para evitar esto, establezca el atributo de funcin alters_data en el mtodo: def delete(self): # Delete the account delete.alters_data = True El sistema de plantillas no ejecutar cualquier mtodo marcado de esta manera. Continuando con el ejemplo actual, si una plantilla incluye {{ account.delete }} y el mtodo delete() tienen alters_data = True, entonces el mtodo delete() no se ejecutar cuando la plantilla se renderice. En su lugar, se producir un error silencioso.
>>> from django.template import Template, Context >>> t = Template(Your name is {{ name }}.) >>> t.render(Context()) uYour name is . >>> t.render(Context({var: hello})) uYour name is . >>> t.render(Context({NAME: hello})) uYour name is . >>> t.render(Context({Name: hello})) uYour name is .
>>> 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
if / else
T
La etiqueta {% if %} evala una variable, y si esa variable es True (es decir, que existe, no est vaco, 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 %}
La etiqueta {% for %} permite un bucle sobre cada elemento de una secuencia. La plantilla renderizar todo entre {% for %} y {% endfor %}. Por ejemplo, podra 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 revs: {% 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 vaca. {% 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 caractersticas que le dan informacin sobre el progreso del bucle:
forloop.counter siempre se establece en un nmero entero que representa el nmero 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 nmero entero que representa el nmero 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 iteracin del bucle. forloop.last es un valor booleano que se establece a True si esta es la ltima iteracin del bucle. forloop.parentloop es una referencia al objeto forloop del bucle padre, en caso de bucles anidados.
T
ifequal / ifnotequal
T
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 %} Slo las variables de plantilla, cadenas, enteros y nmeros decimales se permiten como argumentos para {% ifequal %}. Estos son ejemplos vlidos: {% 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 invlidos:
{% 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 %}. Comentarios
T T
Para designar un comentario usar {# #}. No pueden usar esta sintaxis para varias lneas. Para ello usar la etiqueta {% comment %}, como esto: {% comment %} This is a multiline comment. {% endcomment %} Filtros
T T
Los filtros de plantilla son formas sencillas de alterar el valor de las variables antes de renderizarlas. Los filtros utilizan un carcter de canalizacin (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 maysculas: {{ my_list|first|upper }} Algunos filtros toman argumentos que vienen despus 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 ms 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 parmetro. length: Devuelve la longitud del valor. Para una lista, devuelve el nmero de elementos. Para una cadena, devuelve el nmero de caracteres. Funciona en cualquier objeto Python que sabe cmo determinar su propia longitud, es decir, cualquier objeto que tiene un mtodo __len__()
Filosofas y limitaciones
Ahora que usted tiene una idea del sistema de plantillas de Django, hay que sealar alguna de sus limitaciones intencionales, junto con algo de filosofa de por qu funciona de la forma en que funciona. Ms que cualquier otro componente de las aplicaciones web, la sintaxis de las plantillas es muy subjetiva, y las opiniones de los programadores varan 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 ms conveniente el uso del sistema de plantillas de Django que otras libreras de plantillas Python, pero no es un requisito estricto en ningn sentido. Sin embargo, est claro que tenemos una fuerte preferencia por el lenguaje de plantillas de Django. El sistema de plantillas tiene sus races en la forma en que el desarrollo web se hace en el mundo en lnea combinado con la experiencia de los creadores de Django. Aqu estn algunas de nuestras filosofas:
La lgica de negocio deben estar separada de la lgica de presentacin. Los desarrolladores de Django ven el sistema de plantillas como una herramienta que controla la presentacin y la lgica relacionada con la presentacin, 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 intencin es ser tan til para los formatos no HTML, tales como el texto sin formato. Los diseadores se supone que se sienten cmodos con el cdigo HTML. El sistema de plantillas no est diseado de manera que las plantillas necesariamente se muestran muy bien en los editores WYSIWYG como Dreamweaver. Django espera que los autores de plantillas estn cmodos editando el HTML directamente. Se asume que los diseadores no son programadores de Python. Los autores del sistema de plantillas reconocen que a menudo las plantillas de las pginas web son escritas por los diseadores, no por programadores, y por lo tanto no se debe asumir el conocimiento de Python. El objetivo no es inventar un lenguaje de programacin. El objetivo es ofrecer como mucho la funcionalidad del esquema de programacin, tal como la ramificacin y la iteracin, que es esencial para la toma de decisiones relacionadas con la presentacin.
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 podra 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 sealado. A saber, la plantilla est incrustada en el cdigo Python, por lo que no se logra una verdadera separacin de los datos y la presentacin. Vamos a arreglar esto poniendo la plantilla en un archivo separado, que cargar esta vista. En primer lugar, podra considerar la posibilidad de guardar la plantilla en algn lugar de su sistema de ficheros y usar Python para leer el contenido de la plantilla. Esto es lo que podra 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 doesnt account for missing files! fp = open(/home/djangouser/templates/mytemplate.html) t = Template(fp.read()) fp.close()
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 seala en el cdigo. Si el archivo mytemplate.html no existe o no es legible, la llamada open() lanzar una excepcin IOError. Codifica a pelo la ubicacin de la plantilla. Si usted fuera a utilizar esta tcnica para cada funcin de vista, estara duplicando las localizaciones de la plantilla por no mencionar que se trata de escribir mucho. Incluye una gran cantidad de cdigo 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.
T
Para resolver estos problemas, vamos a utilizar la carga de plantillas y la herencia de plantillas.
T
Carga de Plantillas
Django proporciona un API cmodo 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 configuracin settings.py que hemos mencionado en el captulo anterior, cuando se introdujo la propiedad ROOT_URLCONF.
T T
Abra settings.py y encuentre la propiedad TEMPLATE_DIRS. De forma predeterminada, es una tupla vaca, 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. # Dont 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 aadalo a TEMPLATE_DIRS, as:
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 creacin de un directorio de plantillas dentro de su proyecto (es decir, dentro del directorio que ha creado mysite) Si su TEMPLATE_DIRS slo 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 ambigedad de la tupla de una expresin en parntesis. 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 ms simple es utilizar rutas absolutas (es decir, rutas de directorios que comienzan en la raz del sistema de archivos). Si quiere ser un poco ms flexible, sin embargo, usted puede construir TEMPLATE_DIRS dinmicamente, como en este ejemplo: import os.path TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), templates).replace(\\,'/), ) Este ejemplo utiliza la variable mgica de Python __file__, que se ajusta automticamente al nombre de archivo del mdulo de Python en que reside el cdigo. 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 cdigo 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
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 funcin get_template() toma un nombre de plantilla como argumento, busca dnde 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 excepcin TemplateDoesNotExist.
T T
Ahora, crear el archivo current_datetime.html dentro de su directorio de plantillas mediante el siguiente cdigo de plantilla: It is now {{ current_date }}. Actualice la pgina en el explorador Web, y usted debera ver la pgina completamente renderizada.
render_to_response()
Hemos mostrado cmo 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 codificacin a pelo de las plantillas y las rutas de plantillas. Sin embargo, todava se requiere una buena cantidad de cdigo para escribir todas esas cosas. Debido a que estos pasos son iguales, Django proporciona una abreviatura que le permite cargar una plantilla, renderizarla, y devolver un HttpResponse, todo en una sola lnea de cdigo.
T T
Esta abreviatura es una funcin llamada render_to_response(), que reside en el mdulo django.shortcuts. Aqu est el ejemplo current_datetime reescrito para utilizar render_to_response():
T T
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 creacin de un
contexto para la plantilla. Si usted no proporciona un segundo argumento, render_to_response() utiliza un diccionario vaco.
locals()
Muchas veces, usted se encontrar que usted mismo clcula algunos valores, los almacena en variables (por ejemplo, now en el cdigo anterior), y enva esas variables a la plantilla. Esto es un poco redundante y tambin significa escribir ms.
T T
Usted puede usar la funcin Python llamada locals(). Esta devuelve un diccionario que asigna todos los nombres de variables locales a sus valores, donde local significa todas las variables que han sido definidas en el mbito local. As, la vista anterior podra reescribirse as:
T T T T
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 difcil de manejar el almacenar todas sus plantillas en un solo directorio. Usted podra almacenar plantillas en subdirectorios de su directorio de plantillas, y eso est bien. De hecho, le recomendamos hacerlo; algunas caractersticas ms avanzadas de Django (como el sistema de vistas genricas) esperan esta disposicin de plantillas por defecto. t = get_template(dateapp/current_datetime.html) Ya que render_to_response() es una pequea 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})
{% 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 adicin 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 excepcin TemplateDoesNotExist en una pgina 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 pequeos fragmentos de cdigo HTML, pero en el mundo real usted utilizar el sistema de plantillas de Django para crear pginas enteras de HTML. Esto lleva a un problema de desarrollo web comn: a travs de un sitio Web, cmo se puede reducir la duplicacin y la redundancia de reas de pgina comunes, tales como la navegacin de todo el sitio? Una forma clsica de resolver este problema es usar includes de lado servidor, las directivas que usted puede incrustar dentro de sus pginas HTML para incluir una pgina web dentro de otra. De hecho, Django soporta esta aproximacin con la etiqueta {% include %} que acabamos de describir. Pero la forma preferida de resolver este problema con Django es utilizar una estrategia ms elegante llamado herencia de plantillas.
En esencia, la herencia de plantillas le permite construir una plantilla esqueleto base que contiene todas las partes comunes de su sitio y define bloques que las plantillas hijas puede rellenar.
T T
Veamos un ejemplo de esto creando una plantilla ms completa para nuestra vista current_datetime, editando el archivo current_datetime.html:
T T
<!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, vlida, 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. Imagnese si tuviramos un sitio ms tpico, incluyendo una barra de navegacin, una hojas de estilo, tal vez algo de JavaScript empezaremos metiendo todo ese HTML redundante en cada plantilla.
La solucin 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 header.html:
T T
<!DOCTYPE HTML PUBLIC -//W3C//DTD HTML 4.01//EN> <html lang=en> <head> y quiz almacenar la parte inferior en un fichero llamado footer.html:
T T
<hr> <p>Thanks for visiting my site.</p> </body> </html> Con una estrategia basada en include, los encabezados y los pies de pgina son fciles. Es el punto medio el desordenado. En este ejemplo, ambas pginas tienen un ttulo -<h1>My helpful timestamp site </h1> pero ese ttulo no puede encajar en header.html porque el <title> en ambas pginas es diferente. Si incluimos el <h1> en la cabecera, tendramos que incluir el <title>, que no nos permitira personalizarlo por pgina. El sistema de herencia de plantillas de Django soluciona estos problemas. Puede pensar en ello como una versin dentro-fuera de los includes de lado servidor. En lugar de definir los fragmentos de cdigo que son comunes, se definen los fragmentos de cdigo que son diferentes. El primer paso es definir una plantilla base, un esqueleto de la pgina que las plantillas hijas rellenarn despus. 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>
<p>Thanks for visiting my site.</p> {% endblock %} </body> </html> Esta plantilla, que llamaremos base.html, define un documento de esqueleto HTML sencillo que se utilizar para todas las pginas del sitio. Es el trabajo de las plantillas hijas reemplazar, aadir o dejar vaco el contenido de los bloques.
T T
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 current_datetime.html existente:
T T
{% 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 hours_ahead del Captulo 3. Se podra parecer a esto:
T T
{% 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 slo el cdigo que es nico para esa plantilla. No hay redundancia. Si usted necesita hacer un cambio de diseo de todo el sitio, slo haga el cambio a base.html, y todas las otras plantillas inmediatamente reflejarn el cambio.
T T
He aqu cmo funciona. Cuando se carga la plantilla current_datetime.html, 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, base.html.
T T T T
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, segn sea necesario. Una forma comn de utilizar la herencia es el siguiente enfoque de tres niveles: 1. Crear una plantilla base.html que contenga el diseo principal de su sitio. Esta contiene las cosas que rara vez o nunca se cambian. 2. Crear una plantilla base_SECTION.html por cada seccin del sitio (por ejemplo, base_photos.html y base_forum.html). Estas plantillas extienden a base.html e incluyen estilos y diseo especficos de la seccin. 3. Crear plantillas individuales para cada tipo de pgina, como una pgina del foro o una galera de fotos. Estas plantillas extienden a la plantilla de la seccin correspondiente. Este mtodo maximiza la reutilizacin de cdigo y facilita el aadir elementos a las zonas comunes, como la seccin de navegacin. 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 ms 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 slo los que necesita. Si usted encuentra duplicacin de cdigo en varias plantillas, probablemente significa que usted debe mover el cdigo 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 mgica que proporciona el texto renderizado de la plantilla padre. Esto es til si desea aadir el contenido de un bloque padre en lugar de sobreescribirlo completamente. Usted no puede definir mltiples etiquetas {% block %} con el mismo nombre en la misma plantilla. Esta limitacin existe porque una etiqueta de bloque funciona en ambas direcciones. Es decir, un etiqueta de bloque no slo proporcionan un contenedor para rellenar, sino que tambin define el contenido que rellena el contenedor en el padre. Si hubiese dos etiquetas {% block %} llamadas iguales en una plantilla, el padre no sabra cual de los contenidos de bloque utilizar. El nombre de plantilla que se pasa a {% extends %} se carga utilizando el mismo mtodo que usa get_template(). Es decir, el nombre de la plantilla se aade a la propiedad TEMPLATE_DIRS. En la mayora 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 ejecucin. Esto le permite hacer algunas cosas de forma dinmica.
Tutorial de Django IV
HTU UTH
Modelos
TU UT
Hemos visto los fundamentos de la construccin de sitios Web dinmicos con Django: la configuracin de vistas y URLconfs. Como hemos explicado, una vista es responsable de hacer alguna lgica arbitraria, y luego devolver una respuesta. En uno de los ejemplos, nuestra lgica arbitraria fue calcular la fecha y hora actuales. En las modernas aplicaciones Web, la lgica arbitraria implica a menudo interactar 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 pgina Web. El sitio tambin 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 realizacin de consultas de base de datos usando Python. Este captulo explica esta funcionalidad: la capa de base de datos de Django.
T T
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 parmetros de conexin a la base de datos. Idealmente, esos parmetros deberan almacenarse en la configuracin de Django. Estamos escribiendo una gran cantidad de cdigo redundante: la creacin de una conexin, la creacin de un cursor, la ejecucin de una sentencia, y el cierre de la conexin. Idealmente, todo lo que tendramos 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 parmetros de la conexin, y dependiendo de la naturaleza de la instruccin SQL, posiblemente reescribir el cdigo SQL. Idealmente, el servidor de base de datos que estamos utilizando debera abstraerse, de modo que un cambio de servidor de base de datos podra hacerse en un solo lugar. (Esta caracterstica es especialmente til si usted est construyendo una aplicacin 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 cmo la vista anterior se puede reescribir utilizando la API de base de datos de Django:
T T
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})
Django sigue este patrn MVC lo suficientemente cerca que puede ser denominado un marco MVC. As es ms o menos cmo 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 captulo. V, la parte que selecciona los datos para mostrar y cmo mostrarlo, es manejado por las vistas y las plantillas. C, la parte que delega a una vista en funcin de la entrada del usuario, es manejado por el marco en s, siguiendo su URLconf y llamando a la funcin de Python adecuada para la direccin dada.
Debido a que la C es manejada por el propio marco y la mayor parte de la emocin en Django se produce en los modelos, plantillas, y vistas, Django ha sido denominado como un marco MTV. En el patrn de desarrollo MTV:
M significa modelo, la capa de acceso a datos. Esta capa todo lo relacionado con los datos: cmo acceder a ellos, cmo validarse, las relaciones entre los datos. T representa Plantilla, la capa de presentacin. Esta capa contiene las decisiones relacionadas con la presentacin. V significa Vista, la capa de lgica de negocio. Esta capa contiene la lgica que accede al modelo ylo remite a la plantilla adecuada(s). Usted puede verlo como el puente entre los modelos y plantillas.
Una vez que haya introducido esa configuracin y guarde settings.py, es bueno comprobar su configuracin. Para ello, ejecute python manage.py shell, dentro del directorio del proyecto mysite.
T T T T T T
>>> 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 aplicacin
Ahora que ha verificado que la conexin funciona, es hora de crear una aplicacin Django, un paquete de cdigo Django, que incluya modelos y vistas, que residan juntos en un paquete Python nico y que representan una aplicacin Django completa.
Un proyecto es una instancia de un determinado conjunto de aplicaciones Django, ms la configuracin de esas aplicaciones. Tcnicamente, el nico requisito de un proyecto es que suministra un archivo de configuracin, que define la informacin de conexin de bases de datos, la lista de aplicaciones instaladas, el TEMPLATE_DIRS, y as sucesivamente. Una aplicacin es un conjunto porttil 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 administracin automtica. Un aspecto clave sobre estas aplicaciones es que son portables y reutilizables a travs de mltiples proyectos.
Si usted est utilizando la capa de base de datos de Django (modelos), debe crear una aplicacin Django. Los modelos deben residir dentro de aplicaciones. As que, para empezar a escribir nuestros modelos, tendremos que crear una aplicacin nueva. En el directorio del proyecto mysite, escriba este comando para crear una aplicacin books:
T T T T T
python manage.py startapp books Este comando no produce ningn resultado, pero s crea un directorio books en el directorio mysite.
T T T T
books/ __init__.py models.py tests.py views.py Esto es el estado en blanco de su aplicacin Django.
A fin de proporcionar una API de acceso a datos conveniente, Django necesita saber el diseo de la bases de datos de alguna manera, y hay dos maneras de lograr esto. La primera es describir explcitamente los datos en Python, y la segunda es la introspeccin en la base de datos en tiempo de ejecucin para determinar los modelos de datos. Esta segunda va parece ms limpia, ya que los metadatos residen slo en un lugar, pero introduce algunos problemas. En primer lugar, la introspeccin de una base de datos en tiempo de ejecucin requiere sobrecarga. Si el marco ha de realizar la introspeccin 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 introspeccin precisa y completa. Escribir en Python es divertido, y mantener todo en Python limita el nmero 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 cdigo en lugar de en su base de datos hace que sea ms fcil de mantener sus modelos bajo control de versiones. De esta manera, usted puede fcilmente hacer un seguimiento de los cambios en sus diseos de datos. SQL permite nicamente un grado determinado de metadatos acerca de un diseo de datos. La mayora de los sistemas de base de datos, por ejemplo, no ofrecen un tipo de datos especializado para la representacin de direcciones de e-mail o URL. Los modelos de Django lo hacen. SQL es inconsistente a travs de plataformas de base de datos. Si est distribuyendo una aplicacin Web, por ejemplo, es mucho mejor distribuir un mdulo Python que describa su diseo de datos que conjuntos distintos de sentencias CREATE TABLE para MySQL, PostgreSQL, y SQLite.
Su primer modelo
Nos centraremos en un diseo 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 direccin de correo electrnico. Un editor tiene un nombre, una direccin, ciudad, estado o provincia, un pas, y un sitio web. Un libro tiene un ttulo y una fecha de publicacin. Tambin tiene uno o ms autores (relacin de muchos a muchos con autores) y un solo editor (relacin uno a muchos clave ajena con los editores).
El primer paso del uso de este diseo de base de datos con Django es expresarlo como cdigo Python. En models.py, el archivo que fue creado por el comando startapp, escriba lo siguiente:
T T
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 subclase de django.db.models.Model. 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 cdigo que tiene que escribir para tener acceso bsico a los datos con Django.
T T
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 automticamente, como mostraremos despus. La excepcin 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 ManyToMany 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.
T T
Por ltimo, tenga en cuenta que no hemos definido explcitamente una clave primaria en ninguno de estos modelos. A menos que usted lo indique, Django automticamente le da a cada modelo una clave primaria de entero autonumrico llamado id. Cada modelo de Django necesita tener una clave primaria de una sola columna.
T T
Instalar el modelo
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 adicin de la aplicacin books a la lista de aplicaciones instaladas en el archivo de configuracin.
T T T T
Edite el archivo settings.py de nuevo, y busque la propiedad INSTALLED_APPS, que le dice a Django que aplicaciones estn activas para un proyecto determinado. De forma predeterminada, se ve algo como esto:
T T
INSTALLED_APPS = ( django.contrib.auth, django.contrib.contenttypes, django.contrib.sessions, django.contrib.sites, ) Temporalmente comentemos las cuatro lneas poniendo una almohadilla (#) al principio de ellos. Comentenmos tambin la propiedad por defecto MIDDLEWARE_CLASSES. A continuacin, aadir: mysite.books a la lista INSTALLED_APPS, por lo que la propiedad finalmente podra parecerse a esto:
T T
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 aplicacin Django se ha activado en el archivo de configuracin, podemos crear las tablas de base de datos en nuestra base de datos. En primer lugar, vamos a validar los modelos con la ejecucin de este comando:
T T
python manage.py validate Si sus modelos son vlidos, ejecute el comando siguiente para que Django genere las declaraciones CREATE TABLE de los modelos de la aplicacin books:
python manage.py sqlall books El comando sqlall en realidad no crea las tablas en su base de datos slo imprime en pantalla la salida para que pueda ver el SQL que Django ejecutara. Si quisiera, podra copiar y pegar este SQL en su cliente de base de datos. Sin embargo, Django proporciona una manera ms fcil de llevar a cabo las sentencias SQL en la base de datos: el comando syncdb:
T T T 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 syncdb es una simple sincronizacin de sus modelos a su base de datos. Busca en todos los modelos de cada aplicacin 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 an. Tenga en cuenta que syncdb 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, syncdb no gestionar eso.
T T T T T T
>>> 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=OReilly, 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 mtodo save():
T T
p1 = Publisher() # At this point, p1 is not saved to the database yet! p1.save() # Now it is.
Si desea crear un objeto y guardarlo en la base de datos en un solo paso, use el mtodo objects.create(). Este ejemplo es equivalente al anterior:
T T
>>> 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=OReilly, 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
Puede ver esto en accin aadiendo 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)
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 ms sencilla de hacer cambios en el cdigo actual.) Ahora la lista de objetos Publisher es mucho ms fcil 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 aadir comportamiento a los modelos. Un modelo Django describe ms que el diseo de la tabla de base de datos de un objeto, sino tambin cualquier funcionalidad de un objeto que sepa hacer. __unicode__() es un ejemplo de funcionalidad de este tipo; un modelo sabe cmo mostrarse a s mismo.
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 save():
T T
>>> p.save() Debido a que el modelo utiliza un ID de clave primaria autonumrica, la llamada inicial a save() hace una cosa ms: 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 instruccin SQL UPDATE en lugar de un INSERT). >>> p.name = Apress Publishing >>> p.save() Tenga en cuenta que todos los campos sern actualizados, y no slo los que han sido cambiados.
Seleccionar objetos
Saber cmo crear y actualizar los registros de base de datos es esencial, pero es probable que las aplicaciones web que usted construya sern de hacer ms 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 sera: 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 mayora de los casos, usted querr tratar con un subconjunto de los datos. En la API de Django, puede filtrar los datos utilizando el mtodo filter():
T T
>>> Publisher.objects.filter(name=Apress) [<Publisher: Apress>] filter() toma los argumentos que se traducen en las clusulas where de SQL apropiadas. El ejemplo anterior se traducira en algo como:
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name = Apress; Se pueden pasar mltiples argumentos a filter() para reducir an ms las cosas: >>> Publisher.objects.filter(country=U.S.A., state_province=CA) [<Publisher: Apress>] Los mltiples argumentos se traducen en clusulas 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 bsquedas utilizan el operador = de SQL para hacer bsquedas de concordancia exacta. Otros tipos de bsqueda estn disponibles: >>> Publisher.objects.filter(name__contains=press) [<Publisher: Apress>] Hay un doble subrayado entre name y contains. Aqu, la parte __contains es traducida por Django en una sentencia LIKE de SQL:
T T
SELECT id, name, address, city, state_province, country, website FROM books_publisher WHERE name LIKE %press%; Muchos otros tipos de bsquedas estn disponibles, incluyendo icontains (LIKE insensible a maysculas/minsculas), startswith y endswith, y range (consultas BETWEEN de SQL).
>>> Publisher.objects.get(name=Apress) <Publisher: Apress> Una consulta que devuelva varios objetos producir una excepcin: >>> 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.}
Una consulta que no devuelva objetos tambin causar una excepcin: >>> Publisher.objects.get(name=Penguin) Traceback (most recent call last): DoesNotExist: Publisher matching query does not exist. La excepcin DoesNotExist es un atributo de la clase del modelo: Publisher.DoesNotExist. En sus aplicaciones, usted querr atrapar estas excepciones, de esta forma:
T T
try: p = Publisher.objects.get(name=Apress) except Publisher.DoesNotExist: print Apress isnt 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 alfabtico. Para ello, utilizar el mtodo order_by():
T 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 especfico: 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 mltiples campos usar varios argumentos:
>>> Publisher.objects.order_by(state_province, address) [<Publisher: Apress>, <Publisher: O'Reilly>] Tambin 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 mayora de las veces tendr un campo particular por el querr ordenar. En estos casos, Django permite especificar una ordenacin 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 clase Meta, que es una clase que est incrustada dentro de la definicin de la clase Publisher. Usted puede utilizar la clase Meta en cualquier modelo para especificar opciones especficas del modelo.
T T
Si se especifica esto, le dice a Django que a menos que se indique un ordenamiento de forma explcita 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 bsquedas
Hemos visto como filtrar datos y como ordenarlos. A menudo necesitaremos ambos. En estos casos, encadenar las bsquedas: >>> Publisher.objects.filter(country=U.S.A.).order_by(-name) [<Publisher: O'Reilly>, <Publisher: Apress>]
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 comn es buscar slo un nmero fijo de filas. Imagina que tiene miles de editores en su base de datos, pero que desea mostrar slo 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 especfico 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 fcil de conseguir, sin embargo. Slo cambiar la sentencia order_by() de esta forma: >>> Publisher.objects.order_by(-name)[0]
Hemos sealado en la seccin Insercin y actualizacin de datos que el mtodo save() del modelo actualiza todas las columnas de una fila. Dependiendo de su aplicacin, es posible que desee actualizar slo 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 vera 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 mtodo save() de Django establece todos los valores de las columnas, no slo la columna name. Si est en un entorno en el que otras columnas de la base de datos podran cambiar a causa de algn otro proceso, es ms inteligente cambiar slo la columna que usted necesita. Para ello, utilice el mtodo update() en objetos QuerySet. He aqu un ejemplo: >>> Publisher.objects.filter(id=52).update(name=Apress Publishing) La traduccin a SQL es mucho ms eficiente y no provoca efectos laterales: UPDATE books_publisher SET name = Apress Publishing WHERE id = 52; El mtodo update() funciona en cualquier QuerySet, lo que significa que usted puede editar varios registros de forma masiva. He aqu cmo usted puede cambiar el pas de U.S.A. a USA en todos los registros Publisher:
T T
>>> Publisher.objects.all().update(country=USA) 2 El mtodo update() devuelve un valor: un entero que representa cuntos registros han cambiado. En el ejemplo anterior, fue 2.
Eliminar objetos
Para eliminar un objeto de la base de datos, simplemente llamar al mtodo delete():
T T
>>> p = Publisher.objects.get(name=OReilly) >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>] Tambin puede eliminar objetos de forma masiva llamando al mtodo delete() del resultado de cualquier QuerySet. Esto es similar al mtodo update() que vimos en la ltima seccin: >>> Publisher.objects.filter(country=USA).delete() >>> Publisher.objects.all().delete() >>> Publisher.objects.all() [] Tenga cuidado al borrar sus datos. Como medida de precaucin contra la supresin de todos los datos de una tabla particular, Django requiere que use explcitamente el mtodo 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 aade el mtodo all():
T T
>>> Publisher.objects.all().delete() Si est eliminando slo un subconjunto de datos, no es necesario incluir all(): >>> Publisher.objects.filter(country=USA).delete()
Tutorial de Django V
HTU UTH
Para una cierta clase de sitios Web, una interfaz de administracin 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 adicin, la edicin, y la eliminacin de contenido del sitio.
En tercer lugar, agregue el sitio de administracin a su URLconf (en urls.py, recuerde). Por defecto, el urls.py generado por django-admin.py startproject contiene el cdigo comentado para la administracin de Django, y todo lo que tiene que hacer es quitar los comentarios.
T T T T T T
# 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 configuracin, ahora usted puede ver el sitio de administracin de Django en accin. Simplemente ejecute el servidor de desarrollo (python manage.py runserver) y visite http://127.0.0.1:8000/admin/ en su navegador Web.
T T T T
Una vez que est conectado, lo primero que ver ser la pgina de inicio de administracin. Esta pgina muestra todos los tipos de datos disponibles que se pueden editar en el sitio de administracin. En este punto, ya que no ha activado ninguno de nuestros modelos, la lista es escasa: slo incluye Grupos y Usuarios, que son los dos modelos de administracin por defecto editables. Cada tipo de datos en el sitio de administracin de Django tiene una lista de cambios y un formulario de edicin. Las listas de cambios muestran todos los objetos disponibles en la base de datos, y los formularios de edicin le permiten aadir, cambiar o eliminar registros particulares en su base de datos. Haga clic en el vnculo Cambiar en la fila de los usuarios para cargar la pgina de lista de cambios de los usuarios. Esta pgina muestra todos los usuarios de la base de datos. Pulse un nombre de usuario, y ver el formulario de edicin de ese usuario. Esta pgina le permite cambiar los atributos del usuario.
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 admin.py, y escriba las siguientes lneas de cdigo:
T T
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 cdigo le indica al sitio de administracin de Django que ofrezca una interfaz para cada uno de estos modelos. Una vez que haya hecho esto, vaya a la pgina de inicio de administracin en su explorador Web (http://127.0.0.1:8000/admin/). Usted debe ver una seccin de Libros con enlaces a los autores, libros y editores. Tendr que reiniciar runserver para que los cambios tengan efecto.
T T
Un aspecto digno de mencionar aqu es el manejo de las claves ajenas y las relaciones muchos-a-muchos por el sitio de administracin, las cuales aparecen en el modelo Book. En la pgina Add Book del sitio de administracin de Django (http://127.0.0.1:8000/admin/books/book/add/), el editor (una ForeignKey) es representado por una caja de seleccin, y el campo de los autores (un ManyToManyField) es representado por un cuadro de seleccin mltiple. Ambos campos estn al lado de un signo ms verde que te permite aadir los registros relacionados de ese tipo. Por ejemplo, si hace clic en el signo ms verde junto al campo Publisher, obtendr una ventana emergente que le permite aadir un editor.
En el admin.py de nuestra aplicacin books, cada llamada a admin.site.register() simplemente registra el modelo dado con la administracin. El sitio de administracin mostrar una interfaz de edicin/modificacin para cada modelo que se haya registrado de forma explcita.
T T
La aplicacin django.contrib.auth incluye su propio admin.py, por lo que usuarios y grupos se mostraron de forma automtica en la administracin.
T T
El sitio de administracin de Django es slo una aplicacin 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 cdigo base de Django.
class Author(models.Model): first_name = models.CharField(max_length=30) last_name = models.CharField(max_length=40) email = models.EmailField(blank=True) Al aadir blank = True, hemos comenzado la expansin de nuestro modelo ms all de una simple definicin de la tabla de base de datos. Ahora, nuestra clase de modelo est empezando a convertirse en una rica coleccin de conocimiento sobre lo que los objetos Author son y qu pueden hacer. No slo est el campo email representado por una columna VARCHAR en la base de datos, sino que es tambin un campo opcional en contextos como el sitio de administracin de Django.
inserta una cadena vaca (no un valor NULL) cuando se deja un campo de carcter en blanco. Pero hay una excepcin con los tipos de columna de base de datos que no aceptan cadenas vacas como valores vlidos, tales como fechas, horas y nmeros. Si intenta insertar una cadena vaca en una columna de fecha o nmero entero, lo ms probable es obtener un error de base la de datos. En este caso, NULL es la nica manera de especificar un valor vaco. En los modelos Django, usted puede especificar que se permite NULL aadiendo 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 numrico (por ejemplo, IntegerField, DecimalField, FloatField), tendr que utilizar tanto null = True como blank = True.
T T T T
Cambiemos nuestro modelo Book para permitir una fecha de publicacin 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 ms complicado que aadir blank = True, porque null = True cambia la semntica 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, necesitaremos actualizar la base de datos.
T T
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 cmo quitar el NOT NULL en este caso en particular: ALTER TABLE books_book ALTER COLUMN publication_date DROP NOT NULL; Ahora el formulario de edicin Add Book debera permitir valores de fecha de publicacin vacos.
campo del modelo adecuado. Por ejemplo, as es cmo podemos cambiar la etiqueta del campo Author.email a e-mail, con guin: 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 posicin, para una sintaxis ms 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 ManyToManyField o ForeignKey, debido a que requieren que el primer argumento sea una clase modelo. En esos casos, especificar verbose_name explcitamente.
T T T T
Podemos mejorar este comportamiento predeterminado, aadiendo algunos campos ms a la muestra de la lista de cambios. Sera til, por ejemplo, ver cada e-mail del autor en esta lista, y sera bueno poder ordenarla por nombre y apellido. Para que esto ocurra, vamos a definir una clase ModelAdmin para el modelo de Autor. Esta clase es la clave para personalizar la administracin, y una de las cosas ms bsicas que le permite hacer es especificar la lista de campos a mostrar en las pginas de lista de cambios. Editar admin.py para realizar estos cambios:
T T T T
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 pgina de lista de cambios del autor, y ver que ahora se muestran tres columnas: el nombre, el apellido y la direccin de correo electrnico. Adems, cada una de esas columnas es ordenable haciendo clic en el encabezado de la columna. Ahora agreguemos una barra de bsqueda simple. Aadir a search_fields a AuthorAdmin, as:
T T
class AuthorAdmin(admin.ModelAdmin): list_display = (first_name, last_name, email) search_fields = (first_name, last_name) Actualice la pgina en su navegador, y debera ver una barra de bsqueda en la parte superior. Hemos incluido una barra de bsqueda que busca en los campos first_name y last_name. Ahora agreguemos algunos filtros de fecha para nuestra pgina 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 list_filter, que fija en una tupla los campos a utilizar para crear filtros en el lado derecho de la pgina de lista de cambios. Para los campos fecha, Django proporciona accesos directos para filtrar la lista por Hoy, ltimos 7 das, Este mes, y Este ao.
T T
list_filter tambin trabaja en campos de otros tipos, no slo 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 opcin de administracin date_hierarchy, as:
T T
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 slo un campo de fecha puede utilizarse para la jerarqua. Por ltimo, vamos a cambiar el orden por defecto en que se muestran los libros en la pgina de lista de cambios a siempre ordenado descendente por la fecha de su publicacin. 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 edicin de datos muy potente, lista para produccin, con slo unas pocas lneas de cdigo.
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 opcin fields es que permite excluir a ciertos campos de la edicin. Slo dejando fuera los campo(s) que desea excluir. Por ejemplo, en nuestra base de datos de libros, se podra impedir que el campo publication_date fuese editable: 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) Otro uso comn de personalizar los formularios de edicin tiene que ver con los campos muchos a muchos. El sitio de administracin representa cada ManyToManyField como una caja de seleccin mltiple. Si desea seleccionar varios elementos, hay que mantener pulsada la tecla Control. El sitio de administracin amablemente inserta un fragmento de texto que explica esto, pero, an as, se hace difcil de manejar cuando su campo contiene cientos de opciones. La solucin del sitio de administracin es filter_horizontal.
T T T T
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 ms de diez elementos. Es mucho ms fcil de utilizar.Tambin, recuerde que puede usar filter_horizontal en mltiples campos, especificando solamente cada nombre en la tupla. Las clases ModelAdmin tambin tienen una opcin filter_vertical. Funciona exactamente como filter_horizontal, pero la interfaz coloca las dos casillas verticalmente en vez de horizontalmente. Es una cuestin de gusto personal.
T T T T
filter_horizontal y filter_vertical funcionan slo en los campos ManyToManyField, no en campos ForeignKey. Por defecto, el sitio de administracin usa <select> simples para los campos ForeignKey, pero, como para ManyToManyField, 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 podra tardar bastante en cargar, ya que tiene que cargar cada editor a mostrar en el cuadro <select>.
T T T T T T T T
Puedes solucionar este problema con una opcin llamada raw_id_fields. Ponga esto en una tupla de nombres de campo ForeignKey, y los campos se mostrarn en el administrador con una caja de entrada de texto simple (<input type = text>) en lugar de un <select>.
T T T T
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,)
Qu introducira 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.
El flag activo controla si el usuario est activo. El flag staff controla si el usuario tiene permiso para acceder a la interfaz de administracin. El flag superusuario proporciona al usuario acceso completo a agregar, crear y eliminar cualquier elemento de la interfaz de administracin.
Los usuarios de administracin normales es decir, los miembros activos, no superusuarios se les concede acceso de administracin a travs de los permisos asignados. Cada objeto editable a travs de la interfaz de administracin (por ejemplo, libros, autores, editores) tiene tres permisos: crear, editar y eliminar. La asignacin 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. Tambin 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 concesin de permisos idnticos a un subconjunto de usuarios.
Tutorial de Django VI
HTU UTH
Formularios
TU UT
Los formularios HTML son la columna vertebral de sitios Web interactivos. Este captulo comprende cmo 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.
usuario (generalmente el nombre y la versin 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 direccin IP del cliente.
Usted debe usar una clusula try / except, o un mtodo 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 pequea vista que muestra todos los datos de request.META para que pueda conocer lo que est disponible. Se podra 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))
<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 patrn URL en urls.py podra ser algo como esto:
T T
urlpatterns = patterns(, # (r^search-form/$, views.search_form), # ) Ahora, si ejecuta el runserver y visita http://127.0.0.1:8000/search-form/, ver la interfaz de bsqueda. Bastante simple.
T T
Intente emitir el formulario, sin embargo, y obtendr un error 404 de Django. El formulario apunta a la URL /search/, que an no ha sido implementada. Vamos a arreglar eso con una segunda funcin 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 slo muestra el trmino de bsqueda del usuario de modo que pueda asegurarse que los datos se presentan a Django correctamente y para que pueda tener una idea de cmo los trminos de la bsqueda fluyen a travs 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 enva a travs de GET (method = get) a la URL /search/. 2. La vista Django que se encarga de la direccin /search/ tiene acceso al valor de q en request.GET. Tenga en cuenta que comprobamos explcitamente que q existe en request.GET. Como hemos sealado, usted no debe confiar en nada presentado por los
usuarios. Si no ha aadido esta verificacin, cualquier emisin de un formulario vaco lanzara KeyError en la vista: # BAD! def bad_search(request): # The following line will raise KeyError if q hasnt # 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. Cul es la diferencia entre GET y POST? Utilice GET cuando el acto de emitir el formulario es slo una solicitud para obtener datos. Utilice POST siempre que el acto de emitir el formulario tenga algunos efectos secundarios de actualizacin de datos o enviar un email, o algo ms de la simple exhibicin de los datos. En nuestro ejemplo de bsqueda de libro, estamos utilizando GET porque la consulta no cambia ningn dato en nuestro servidor. Ahora que hemos verificado que request.GET se est pasando correctamente, vamos a conectar la consulta de bsqueda del usuario con nuestra base de datos de libros (de nuevo, en views.py):
T 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 cdigo de la plantilla search_results.html podra ser algo como esto:
T T
<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 %}
</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> Todava 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 aplicacin mejor, pero ahora surge la pregunta: es una vista search_form() dedicada realmente necesaria? Tal como est, una solicitud a la direccin URL /search/ (sin los parmetros GET) mostrar el formulario vaco (pero con un error). Podemos eliminar la vista search_form(), junto con su patrn URL asociado, siempre y cuando cambiemos search() para ocultar el mensaje de error cuando alguien visite /search/ sin parmetros 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 parmetros GET, ver el formulario de bsqueda sin mensaje de error. Si un usuario enva el formulario con un valor vaco para q, ver el formulario de bsqueda con un mensaje de error. Y, por ltimo, si un usuario enva el formulario con un valor no vaco de q, ver los resultados de bsqueda.
Podemos hacer una mejora final a esta aplicacin, 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 bsqueda como la del resultado, el formulario HTML en search_form.html no tiene que codificar una URL a pelo. En lugar de esto:
T T
puede cambiarse a: El action = significa Enviar el formulario a la misma URL que la pgina actual. Con este cambio, usted no tendr que acordarse de cambiar la accin, incluso si alguna vez la vista search() apunta a otra URL.
Validacin simple
Nuestro ejemplo de bsqueda es todava bastante simple, especialmente en trminos de validacin de sus datos, hacemos una mera comprobacin para asegurar que la consulta de bsqueda no est vaca. Muchos de los formularios HTML incluyen un nivel de validacin que es ms complejo que asegurar que el valor no est vaco. Todos hemos visto la siguientes mensajes de error en los sitios Web: Por favor introduzca una direccin de correo electrnico. Por favor, introduzca un cdigo postal de cinco dgitos vlido EE.UU. Por favor, introduzca una fecha vlida con formato AAAA-MM-DD. Por favor, introduzca una contrasea que sea por lo menos de 8 caracteres de longitud y contenga al menos un nmero. Vamos a afinar la vista search() para validar que el trmino de bsqueda sea menor o igual a 20 caracteres de largo. Cmo podemos hacerlo? Lo ms sencillo sera integrar la lgica directamente en la vista, as: def search(request): error = False if q in request.GET: q = request.GET['q'] if not q: error = True elif len(q) > 20: error = True else: books = Book.objects.filter(title__icontains=q) return render_to_response(search_results.html,{books: books, query: q}) return render_to_response(search_form.html, {error: error})
Ahora bien, si se intenta emitir una consulta de bsqueda mayor de 20 caracteres de largo, obtendr un mensaje de error. Pero ese mensaje de error en search_form.html actualmente dice: Por favor, introduzca un trmino de bsqueda., as que tendremos que cambiarlo para ser exactos en ambos casos (una bsqueda vaca o un trmino de bsqueda demasiado largo).
T T
<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 vaco menciona nada sobre un lmite de 20 caracteres? Los mensajes de error deben ser especficos y claros. El problema es que estamos utilizando un solo valor boolean para el error, cuando habra que utilizar una lista de cadenas de mensajes de error. He aqu cmo podemos solucionarlo: def search(request): errors = [] if q in request.GET: q = request.GET['q'] if not q: errors.append(Enter a search term.) elif len(q) > 20:
errors.append(Please enter at most 20 characters.) else: books = Book.objects.filter(title__icontains=q) return render_to_response(search_results.html, {books: books, query: q}) return render_to_response(search_form.html, {errors: errors}) Entonces tenemos que hacer un pequeo ajuste en la plantilla search_form.html para reflejar que se pas una lista de errores, en lugar de un error boolean:
T T
<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>
<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 direccin de correo electrnico 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 emisin tiene un efecto secundario que enva un e-mail. Si seguimos el camino establecido por nuestra vista search() de la seccin anterior, una versin de nuestra vista contact() podra tener este aspecto: from django.core.mail import send_mail from django.http import HttpResponseRedirect from django.shortcuts import render_to_response def contact(request): errors = [] if request.method == POST: if not request.POST.get(subject, ):
errors.append(Enter a subject.) if not request.POST.get(message, ): errors.append(Enter a message.) if request.POST.get(e-mail) and @ not in request.POST['e-mail']: errors.append(Enter a valid e-mail address.) if not errors: send_mail(request.POST['subject'], request.POST['message'], request.POST.get(e-mail, [email protected]), ['[email protected]'], ) return HttpResponseRedirect(/contact/thanks/) return render_to_response(contact_form.html,{errors: errors}) Varias cosas nuevas estn sucediendo aqu:
Comprobamos que request.method es POST. Esto ser cierto slo en el caso de una emisin 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 detransformacin del formulario. En lugar de request.GET, estamos usando request.POST para acceder a los datos del formulario emitido. Esto es necesario porque el cdigo 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 electrnico no es obligatorio, debemos an validarlo si es emitido. Nuestro algoritmo de validacin aqu es frgil estamos comprobando que la cadena contiene un carcter @. En el mundo real, necesitaramos una validacin ms robusta. Estamos usando la funcin django.core.mail.send_mail para enviar un email. Esta funcin tiene cuatro argumentos necesarios: el asunto del e-mail, el cuerpo del correo electrnico, la direccin del emisor, y una lista de direcciones de los destinatarios. send_mail est contenida en la clase de Django EmailMessage, que proporciona caractersticas avanzadas tales como archivos adjuntos, emails multiplart, y el control total de los encabezados del correo electrnico. Despus de enviar el e-mail, redirigimos a una pgina de xito devolviendo un objeto HttpResponseRedirect. Usted siempre debe enviar una redireccin para el xito de las peticiones POST. Es una de las mejores prcticas del desarrollo web.
Esto vista funciona, pero las funciones de validacin son enrevesadas. Imagine la tramitacin 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 validacin, es mejor prctica 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 podramos pasar los datos POST de nuevo a la plantilla, pero habra 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>
<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 cdigo, e introduce un montn de posibilidades de error humano. Veremos alguna librera de alto nivel que gestione las tareas relacionadas con los formularios y la validacin.
HTML. En nuestro caso, slo tenemos una , as que tendremos una clase Form. Esta clase puede residir en cualquier lugar, incluyendo directamente en el archivo views.py, pero la convencin es mantener las clases Form en un archivo llamado forms.py. Cree este archivo en el mismo directorio que el views.py, y escriba lo siguiente:
T T T T
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 intrprete 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 aade 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() <p><label for=id_subject>Subject:</label> <input type=text name=subject id=id_subject /></p>
<p><label for=id_e-mail>E-mail:</label> <input type=text name=e-mail id=id_e-mail /></p> <p><label for=id_message>Message:</label> <input type=text name=message id=id_message /></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 mtodos son mtodos abreviados para el caso comn de mostrar todo el formulario. Usted tambin puede mostrar el cdigo 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 mtodo is_valid() de cualquier Form ligado para averiguar si sus datos son vlidos. Hemos pasado un valor vlido para cada campo, por lo que el formulario en su totalidad es vlido: <<< f.is_valid() True Si no se pasa el campo de correo electrnico, sigue siendo vlido, 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 vlido: <<< f = ContactForm({subject: Hello}) <<< f.is_valid() False <<< f = ContactForm({subject: Hello, message: })
<<< f.is_valid() False Usted puede ver los detalles obteniendo mensajes de error especficos 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 vlidos, est disponible el atributo cleaned_data. Este es un diccionario de los datos emitidos, limpiado. El marco de formularios de Django, no slo 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: uNice site!, e-mail: [email protected], subject: uHello} Nuestro formulario de contacto trata slo con cadenas, que son limpiadas a objetos Unicode, pero si tuviramos que utilizar un IntegerField o un DateField, el marco de formularios garantizara que cleaned_data utiliza enteros Python adecuados u objetos datetime.date para los campos dados.
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>
</form> </body> </html> El marco de formularios de Django gestiona la pantalla del HTML, la validacin, la limpieza de los datos, y el volver a mostrar el formulario con errores. Trate de ejecutar esto en local. Cargue el formulario, emtalo sin rellenar ningun campo, emtalo con una direccin invlida de correo electrnico, y finalmente emtalo con los datos vlidos.
Como una mejora a este formulario, vamos a aadir 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 slo pasa los datos iniciales, el formulario ser independiente, lo que significa que no tendr ningn mensaje de error.
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 automticamente cualquier mtodo cuyo nombre empieza con clean_ y termina con el nombre de un campo. Si existe alguno, se llama durante la validacin. En concreto, el mtodo clean_message() ser llamado despus de la lgica de validacin por defecto para un campo determinado (en este caso, la lgica de validacin para un CharField obligatorio).Debido a que los datos del campo ya han sido parcialmente procesados, hay que sacarlos de self.cleaned_data. Adems, no tiene que preocuparse de comprobar que el valor existe y no es vaco, el validador lo hace de forma predeterminada. Nosotros, utilizamos una combinacin de len() y split() para contar el nmero de palabras. Si el usuario ha introducido muy pocas palabras, lanzamos un forms.ValidationError. La cadena adjunta a la excepcin se muestra al usuario como un elemento de la lista de errores. Es importante que devolvamos explcitamente el valor limpiado para el campo al final del mtodo. Esto nos permite modificar el valor (o convertirlo a un tipo diferente de Python) dentro de nuestro mtodo de validacin personalizado. Si olvidamos de la instruccin 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 sustitucin 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. Slo 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)
Nuestra plantilla contact_form.html usa {{ form.as_table }} para mostrar el formulario, pero podemos visualizarlo de otras formas para conseguir un control ms detallado sobre la pantalla.
T T
La forma ms rpida para personalizar la presentacin de los formularios es con CSS. Las listas de error, en particular, podan 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 representacin 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 estn disponibles como {{ form.fieldname.errors }}. Con esto en mente, podemos construir una plantilla personalizada para nuestro formulario de contacto con el siguiente cdigo de plantilla: <html> <head> <title>Contact us</title> </head> <body>
<h1>Contact us</h1> {% if form.errors %} <p style=color: red;> Please correct the error{{ form.errors|pluralize }} below. </p> {% endif %} <form action=" method=post> <div class=field> {{ form.subject.errors }} <label for=id_subject>Subject:</label> {{ form.subject }} </div> <div class=field> {{ form.e-mail.errors }} <label for=id_e-mail>Your e-mail address:</label> {{ form.e-mail }} </div> <div class=field> {{ form.message.errors }} <label for=id_message>Message:</label> {{ form.message }} </div> <input type=submit value=Submit> </form> </body> </html> {{ form.message.errors }} muestra una <ul class=errorlist> si hay errores y una cadena en blanco si el campo es vlido (o el formulario no est ligado). Tambin puede tratar form.message.errors como un valor booleano o incluso iterar sobre l como una lista. Considere este ejemplo: <div class=field{% if form.message.errors %} errors{% endif %}>
{% if form.message.errors %} <ul> {% for error in form.message.errors %} <li><strong>{{ error }}</strong></li> {% endfor %} </ul> {% endif %} <label for=id_message>Message:</label> {{ form.message }} </div> En el caso de errores de validacin, esto aadir una clase errors al <div> y muestra la lista de errores en una lista desordenada.
Trucos URLconf
No hay nada especial en URLconfs como todo en Django, slo cdigo Python. Racionalizar las importaciones de funciones Considerar la siguiente URLconf, basada en un ejemplo visto anteriormente:
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 funcin. Esto significa que es necesario importar la vista al prinicipio del mdulo. Pero, cuando una aplicacin de Django crece en complejidad, su URLconf tambin crece, y gestionar esas importaciones puede ser tedioso. (Para cada nueva vista, usted tendra que recordar importarla, y la declaracin de importacin tiende a ser demasiado larga si se utiliza este enfoque.) Es posible evitar esto con la importacin de propio mdulo 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 patrn particular en el URLconf: se puede pasar una cadena que contenga el nombre del mdulo y el nombre de la vista en lugar del objeto de funcin 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.
Usando esta tcnica, ya no es necesario importar las vistas; Django automticamente importa la funcin de vista apropiada la primera vez que se necesita, segn la cadena que describe el nombre y la ruta de la funcin de vista. Una abreviatura ms que usted puede usar cuando utiliza esta tcnica es factorizar un prefijo de vista comn. En nuestro ejemplo de URLconf, cada una de las cadenas de vista comienza con mysite.views. Podemos factorizar este prefijo comn 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, cul es mejor? Realmente depende de su estilo personal de programacin y de sus necesidades. Las ventajas de la cadena son las siguientes:
Es ms compacto, porque no es necesario importar las funciones de vista. El resultado es URLconfs ms legibles y manejables si las funciones de su vista se reparten entre diferentes mdulos de Python.
Se permite un fcil ajuste de las funciones de vista. Es ms acorde con las tradiciones de Python, como pasar funciones como objetos.
Ambos enfoques son vlidos, e incluso se pueden mezclar en el mismo URLconf. La eleccin es suya.
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 peticin a /articles/2006/03/ dara lugar a una llamada a la funcin equivalente a esto: month_archive(request, 2006, 03) Con grupos con nombre, la misma peticin resultara en esta llamada a funcin: month_archive(request, year=2006, month=03) En la prctica, usar grupos con nombre hace a su URLconfs un poco ms explcita y menos propensa a los errores del orden de los argumentos y puede volver a ordenar los argumentos de la funcin sus definiciones de vistas. Siguiendo el ejemplo anterior, si quisieramos cambiar la URL para incluir el mes antes del ao, y estamos utilizando grupos sin nombre, tendramos que recordar cambiar el orden de los argumentos en la vista month_archive. Si estuviramos utilizando grupos con nombre, cambiar el orden de los parmetros capturados en la URL no tendra ningn 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 estn ntimamente familiarizados con expresiones
regulares o su aplicacin de Django particular. Es ms fcil ver lo que sucede, de un vistazo, en una URLconf que utiliza grupos con nombre.
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.
URL, poniendo entre parntesis 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 solucin, sin embargo, es que acopla las URL a su cdigo. Si usted decide cambiar el nombre /foo/ a /fooey/, tendr que acordarse de cambiar el cdigo de la vista. La solucin elegante implica un parmetro URLconf opcional. Cada patrn en una URLconf puede incluir un tercer elemento: un diccionario de los argumentos de palabra clave para pasar a la funcin 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}), )
# 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 tcnica de opciones URLconf extra es una buena forma de enviar informacin adicional a las funciones de la vista con una sobrecarga mnimo. Las siguientes secciones contienen un par de ideas sobre cmo utilizar la tcnica de opciones de URLconf extra en sus propios proyectos.
Por ejemplo, usted podra querer agregar otra URL, /mydata/birthday/, que sera 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 funcin de vista en absoluto.
# 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 pequeos 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 parmetro model. El diccionario de opciones URLConf extra puede pasar cualquier tipo de objeto no slo 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 funcin es til en momentos como ste, cuando no sabemos el tipo de clase hasta el tiempo de ejecucin.
Pasamos el nombre de variable genrico object_list a la plantilla. Fcilmente podramos 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 genricas que utilizan esta tcnica para ahorrar tiempo.
# 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 views.page, pero el primer patrn no captura cualquier cosa desde la URL. Si el primer patrn concuerda, la funcin page() usar su argumento predeterminado para num, 1. Si el segundo patrn concuerda, page() utilizar cualquier valor num capturado por la expresin regular.
T T
Es comn el uso de esta tcnica en relacin con las opciones de configuracin, como se explic anteriormente. En el siguiente ejemplo se hace una ligera mejora con el ejemplo de la seccin Dar opciones de configuracin de vista, proporcionando un valor predeterminado para template_name:
T T
Esto coincide con las URL como /myblog/entries/add/ y /auth/groups/add/. Sin embargo, la pgina Aadir para un objeto de usuario (/auth/user/add/) es un caso especial que no muestra todos los campos del formulario, muestra dos campos de contrasea, etc. Podramos 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 razn que hemos visto varias veces en este captulo: pone lgica de URL en la vista. Como una solucin ms 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 peticin a /auth/user/add/ ser manejada por la vista user_add_stage. A pesar de que la URL coincide con el segundo patrn, coincide con el de arriba primero.
TypeError: an integer is required >>> datetime.date(1993, 7, 9) datetime.date(1993, 7, 9) Trasladado a una URLConf y vista, el error se parecera 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() podra 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 dgitos, pero estamos evitando el error en este caso porque la expresin regular en nuestra URLconf asegura que slo se pase cadenas que contienen dgitos a la funcin de vista.
# 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 segn 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 funcin, se eleva un Http404.
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 lgica en una sola vista.
Ahora tenemos una bonita funcin de vista genrica que encapsula la lgica de delegar una vista mediante request.method. Nada de method_splitter() est ligado a nuestra aplicacin especfica, 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 caracterstica 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 caracterstica Python que permite a una funcin aceptar un nmero arbitrario dinmico de argumentos cuyos nombres no se conocen hasta el tiempo de ejecucin. Si coloca un asterisco delante de un parmetro en una definicin de la funcin, cualquier argumento posicional a esa funcin se guarda en una sola tupla. Si coloca dos asteriscos delante de un parmetro en una definicin de la funcin, cualquier argumento de palabras clave para esa funcin se guardar en un diccionario nico. Por ejemplo, tenga en cuenta esta funcin: def foo(*args, **kwargs): print Positional arguments are: print args print Keyword arguments are: print kwargs
As es como funcionara: >>> 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 funcin 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 estn disponibles. (Estamos usando pop() con un valor por defecto None para evitar KeyError si alguno no est definido.)
# 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/. Sera bueno si pudiramos eliminar ese trozo de cdigo repetitivo de cada una de estas vistas y slo marcarlos como que requieren autenticacin. 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 funcin, requires_login, toma una funcin de vista (view) y devuelve una nueva funcin de vista (new_view). La nueva funcin, new_view, se define dentro de requires_login y se encarga la lgica 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 cdigo redundante. Ahora hemos creado una buena funcin genrica requires_login() que nos puede envolver cualquier vista para requerir un inicio de sesin.
De esta forma, su URLconf puede incluir otros mdulos 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 enva 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 gestionaran algunas solicitudes:
/weblog/2007/: En la primera URLconf, el patrn 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 direccin es 2007/, que coincide con la primera lnea en el URLconf mysite.blog.urls. /weblog//2007/ (con dos barras inclinadas): En la primera URLconf, el patrn 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 direccin es /2007/, que no coincide con ninguna de las lneas 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.
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 funcin de vista dentro de ese URLconf. Tenga en cuenta que los parmetros capturados siempre sern pasados a todas las lneas del URLconf incluido, independientemente de si la vista de la lnea en realidad acepta esos parmetros como vlidos. Por esta razn, esta tcnica es til slo si ests seguro de que todas las vistas de la URLconf incluida aceptan los parmetros que est pasando.
(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 parmetros capturados, las opciones adicionales siempre se pasarn a todas las lneas del URLconf incluido, independientemente de si la vista de la lnea acepta esas opciones como vlidas. Por esta razn, esta tcnica es til slo si tiene la certeza de que todas las vistas incluidas en la URLconf aceptan las opciones extra que est pasando.