You are on page 1of 381

El libro de Django Autores: Adrian Holovaty y Jacob Kaplan-Moss Editor técnico: Jeremy Dunck

NOTA: Este trabajo de traducción aún no está completo. Número de revisión: 789 Fecha de compaginación: 9 de noviembre de 2009

Índice general
1. Preliminares
1.1. 1.2. 1.3. 1.4. 1.5. Reconocimientos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sobre los autores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Sobre el editor técnico Sobre el libro Sobre los traductores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

xvii
xvii xvii xviii xviii xviii

2. Introducción 3. Introducción a Django
3.1. 3.2. 3.3. 3.4. ¾Qué es un Framework Web? El patrón de diseño MVC La historia de Django Cómo leer este libro 3.4.1. 3.4.2. 3.4.3. 3.4.4. 3.5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

xix

1
1 2 3 4 5 5 5 5 5

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Conocimientos de programación requeridos

Conocimientos de Python requeridos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nuevas características de Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Obteniendo ayuda . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

4. Empezando
4.1. 4.2. Instalar Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Instalar Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2.1. 4.2.2. 4.3. 4.3.1. 4.3.2. 4.3.3. 4.3.4. 4.4. 4.5. 4.4.1. Instalar un lanzamiento ocial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Instalar Django desde Subversion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Usar Django con PostgreSQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Usar Django con SQLite 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Usar Django con MySQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Usar Django sin una base de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . El servidor de desarrollo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7
7 7 7 8 8 9 9 9 9 9 10 11

Congurar la base de datos

Comenzar un proyecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

5. Los principios de las páginas Web dinámicas
5.1. 5.2. 5.3. 5.4. 5.5. 5.6. Tu primera Vista: Contenido dinámico . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Mapeando URLs a Vistas 5.3.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Cómo procesa una petición Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Cómo procesa una petición Django: Detalles completos URLconfs y el acoplamiento débil . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Errores 404 5.6.1. 5.6.2. 5.7. 5.8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Tu segunda Vista: URLs dinámicas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Algunas palabras acerca de las URLs bonitas . . . . . . . . . . . . . . . . . . . . . . . . . . . . Comodines en los patrones URL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13
13 14 16 16 18 18 20 20 20 22 23

Páginas de error bonitas con Django

¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

revisión 789 del 9 de noviembre de 2009

iv

ÍNDICE GENERAL

6. El sistema de plantillas de Django
6.1. 6.2. Sistema básico de plantillas 6.2.1. 6.2.2. 6.2.3. 6.2.4. 6.2.5. 6.3. 6.3.1. 6.3.2. 6.4. 6.5. 6.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Empleo del sistema de plantillas

25
25 26 26 28 29 29 32 32 32 36 37 38 39 42 42 43 43 43 46

Creación de objetos Template . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Renderizar una plantilla . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Múltiples contextos, mismas plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Búsqueda del contexto de una variable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Jugando con objetos Context . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Etiquetas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Etiquetas básicas de plantillas y ltros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

Filosofía y Limitaciones Cargadores de plantillas 6.6.1. 6.6.2. 6.6.3. 6.6.4. El truco locals()

Uso de plantillas en las vistas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . render_to_response() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Subdirectorios en get_template() . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . La etiqueta de plantilla

include

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

6.7. 6.8.

Herencia de plantillas

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7. Interactuar con una base de datos: Modelos
7.1. 7.2. 7.3. 7.4. 7.5. 7.6. 7.7. 7.8. 7.9. La manera tonta de hacer una consulta a la base de datos en las vistas . . . . . . . . . . . . . . . . . El patrón de diseño MTV Tu primera aplicación Tu primer modelo Instalar el modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Conguración de la base de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Denir modelos en Python . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

47
47 48 49 51 52 52 54 56 57 58 58 59 60 60 61 61 62 62 63 64 64 64 65

Acceso básico a datos

Agregar strings de representación del modelo

7.10. Insertar y actualizar datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.11. Seleccionar objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.11.1. Filtrar datos 7.11.3. Ordenar datos 7.11.5. Rebanar datos 7.12. Eliminar objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.11.2. Obtener objetos individuales 7.11.4. Encadenar búsquedas

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

7.13. Realizar cambios en el esquema de una base de datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.13.1. Agregar campos 7.13.2. Eliminar campos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.13.3. Eliminar campos Many-to-Many 7.13.4. Eliminar modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7.14. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

8. El sitio de Administración Django
8.1. 8.2. 8.3. 8.4. 8.5. 8.6. 8.7. Activar la interfaz de administración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Usar la interfaz de administración 8.2.1. Usuarios, Grupos y Permisos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

67
67 68 73 73 77 79 79 79

Personalizar la interfaz de administración

Personalizar la apariencia de la interfaz de administración . . . . . . . . . . . . . . . . . . . . . . . . . Personalizar la página índice del administrador Cuándo y porqué usar la interfaz de administración . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

revisión 789 del 9 de noviembre de 2009

ÍNDICE GENERAL

v

9. Procesamiento de formularios
9.1. 9.2. 9.3. 9.4. 9.5. 9.6. 9.7. 9.8. Búsquedas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . El formulario perfecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Creación de un formulario para comentarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Procesamiento de los datos suministrados Nuestras propias reglas de validación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Una presentación personalizada . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Creando formularios a partir de Modelos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

81
81 83 83 86 87 88 89 90

10.Vistas avanzadas y URLconfs
10.1. Trucos de URLconf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.1. Importación de funciones de forma efectiva 10.1.2. Usar múltiples prejos de vista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

91
91 91 92 93 93 95 95 99 100 100 101 101 102 102 103

10.1.3. Casos especiales de URLs en modo Debug . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.4. Usar grupos con nombre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.5. Comprender el algoritmo de combinación/agrupación . . . . . . . . . . . . . . . . . . . . . . . . 10.1.6. Pasarle opciones extra a las funciones vista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.7. Usando argumentos de vista por omisión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.8. Manejando vistas en forma especial . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.9. Capturando texto en URLs 10.2. Incluyendo otras URLconfs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10.1.10. Entendiendo dónde busca una URLconf

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

10.2.1. Cómo trabajan los parámetros capturados con include() . . . . . . . . . . . . . . . . . . . . . . 10.2.2. Cómo funcionan las opciones extra de URLconf con include() . . . . . . . . . . . . . . . . . . . 10.3. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11.Vistas genéricas
11.1. Usar vistas genéricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.2. Vistas genéricas de objetos 11.3. Extender las vistas genéricas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

105
105 106 107 108 108 109 109 110 111

11.3.1. Crear contextos de plantilla amistosos 11.3.3. Mostrar subconjuntos de objetos 11.3.5. Realizar trabajo extra

11.3.2. Agregar un contexto extra . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11.3.4. Filtrado complejo con funciones adaptadoras

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

11.4. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12.Extender el sistema de plantillas
12.1. Revisión del lenguaje de plantillas 12.2. Procesadores de contexto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

113
113 113 117 117 117 117 117 118 119 119 120 121 125 125 126 127 128 128

12.2.1. django.core.context_processors.auth . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.2.2. django.core.context_processors.debug 12.2.3. django.core.context_processors.i18n 12.2.4. django.core.context_processors.request 12.3. Detalles internos de la carga de plantillas 12.4. Extender el sistema de plantillas

12.2.5. Consideraciones para escribir tus propios procesadores de contexto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

12.4.1. Crear una biblioteca para plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.4.2. Escribir ltros de plantilla personalizados 12.4.3. Escribir etiquetas de plantilla personalizadas 12.4.5. Etiquetas de inclusión

12.4.4. Un atajo para etiquetas simples . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.5. Escribir cargadores de plantillas personalizados

12.6. Usar la referencia de plantillas incorporadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12.7. Congurar el sistema de plantillas en modo autónomo 12.8. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

revisión 789 del 9 de noviembre de 2009

vi

ÍNDICE GENERAL

13.Generación de contenido no HTML
13.1. Lo básico: Vistas y tipos MIME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.2. Producción de CSV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3. Generar PDFs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3.1. Instalar ReportLab . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3.2. Escribir tu Vista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.3.3. PDFs complejos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.4. Otras posibilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5. El Framework de Feeds de Sindicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.1. Inicialización . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.2. Un Feed simple . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.3. Un Feed más complejo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.4. Especicar el tipo de Feed . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.5. Enclosures . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.6. Idioma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.7. URLs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.5.8. Publicar feeds Atom y RSS conjuntamente 13.6. El framework Sitemap 13.6.1. Instalación 13.6.2. Inicialización

129
129 130 131 131 131 132 133 133 133 134 135 136 137 137 137 137 138 138 139 139 140 141 141 142

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

13.6.3. Clases Sitemap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.6.4. Accesos directos 13.6.5. Crear un índice Sitemap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.6.6. Hacer ping a Google . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.7. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14.Sesiones, usuario e inscripciones
14.1. Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.1.1. Cómo denir y leer los valores de las cookies

143
143 144 145 145 146 146 147 148 148 149 149 150 151 151 153 154 155 156 158 159 159 160 160 161 161

14.1.2. Las cookies tienen doble lo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2. El entorno de sesiones de Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.1. Activar sesiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.2. Usar las sesiones en una vista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.3. Comprobar que las cookies sean utilizables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.4. Usar las sesiones fuera de las vistas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.5. Cuándo se guardan las sesiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.6. Sesiones breves frente a sesiones persistentes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.2.7. Otras características de las sesiones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.3. Usuarios e identicación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.3.1. Habilitando el soporte para autenticación 14.4.1. Iniciar y cerrar sesión . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.4. Utilizando usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.4.2. Limitar el acceso a los usuarios identicados . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 14.4.3. Limitar el acceso a usuarios que pasan una prueba 14.4.4. Gestionar usuarios, permisos y grupos 14.4.5. Usar información de autenticación en plantillas 14.5. El resto de detalles: permisos, grupos, mensajes y perles 14.5.2. Grupos 14.5.3. Mensajes 14.5.4. Perles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14.5.1. Permisos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

14.6. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

revisión 789 del 9 de noviembre de 2009

ÍNDICE GENERAL

vii

15.Cache
15.1. Activar el Cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1.1. Memcached . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1.2. Cache en Base de datos 15.1.3. Cache en Sistema de Archivos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1.4. Cache en Memoria local . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1.5. Cache Simple (para desarrollo) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1.6. Cache Dummy (o estúpida) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.1.7. Argumentos de CACHE_BACKEND 15.2. La cache por sitio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.3. Cache por vista . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.3.1. Especicar la cache por vista en URLconf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.4. La API de cache de bajo nivel . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.5. Caches upstream . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.5.1. Usar el encabezado Vary . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.5.2. Otros Encabezados de cache . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.6. Otras optimizaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.7. Orden de MIDDLEWARE_CLASSES . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15.8. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

163
163 164 164 165 165 165 165 165 166 167 167 168 169 169 170 171 171 172

16.Otros sub-frameworks contribuidos
16.1. La biblioteca estándar de Django . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2. Sites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.1. Escenario 1: reuso de los datos en múltiples sitios . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.2. Escenario 2: alojamiento del nombre/dominio de tu sitio en un solo lugar . . . . . . . . . . . . 16.2.3. Modo de uso del framework sites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.4. Las capacidades del framework Sites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.5. CurrentSiteManager 16.3. Flatpages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.2.6. El uso que hace Django del framework Sites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.3.1. Usar atpages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.3.2. Agregar, modicar y eliminar atpages . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.3.3. Usar plantillas de atpages 16.4. Redirects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

173
173 174 174 174 174 175 177 178 178 179 180 180 181 181 181 182 182 182 183 184 184 184 184 184 185 185

16.4.1. Usar el framework redirects . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.4.2. Agregar, modicar y eliminar redirecciones 16.5.1. Un ejemplo simple de CSRF 16.5. Protección contra CSRF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.5.2. Un ejemplo más complejo de CSRF

16.5.3. Previniendo la CSRF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.6. Hacer los datos más humanos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.6.1. apnumber . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.6.2. intcomma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.6.3. intword . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.6.4. ordinal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.7. Filtros de marcado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16.8. ¾Qué sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

17.Middleware
17.1. Qué es middleware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17.2. Instalación de Middleware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17.3. Métodos de un Middleware . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17.3.1. Inicializar: __init__(self )

187
187 188 188 188 188 189 189 189 190 190

17.3.2. Pre-procesador de petición: process_request(self, request) . . . . . . . . . . . . . . . . . . . . . 17.3.3. Pre-procesador de vista: process_view(self, request, view, args, kwargs) 17.3.4. Pos-procesador de respuesta: process_response(self, request, response) . . . . . . . . . . . . . . 17.3.5. Pos-procesador de excepción: process_exception(self, request, exception) . . . . . . . . . . . . . 17.4. Middleware incluido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17.4.1. Middleware de soporte para autenticación . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . revisión 789 del 9 de noviembre de 2009

. . Integración con una base de datos existente 18. . . . . . . . . . . . . . . . . . . . . . 20. . . . . .. . . . . . . . . . . . . . . . . . . contenido estructurado 19. . . . . . . . . .7. . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . Crear vistas de administración personalizadas . . . . . . .1. . . . .1. . . . . . . . . Compilando archivos de mensajes . . Notas para usuarios familiarizados con 20. . . . 17. . . . . . . . . . . . . . .3. . . . .. . . . . . .5. . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19. . . . . . . . . . . . 20. Sobreescribir vistas incorporadas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. .2. . . Escribir un back-end de autenticación .. . Middleware de compresión . . . . . . . . . . . . . . . . . ¾Qué sigue? . . . . . . . . . . . 18. . . . . . . . . . . . 20. . . . . .2. . . . . . . . 17. . . . . . . . . . . JavaScript Personalizado 19. . 19. . . . . . . . . . . . . . . . . .Integración con Base de datos y Aplicaciones existentes 18. . . . . . . Soporte para uso de proxy inverso (Middleware X-Forwarded-For) 190 190 191 191 191 191 191 191 191 17. . . . . . . . . . . . . . . . . . . . . . . . . . . . Middleware de soporte para sesiones . . . . . Middleware de cache de todo el sitio . . . . .1. . . . . . . . .4. . . . Limpiar los modelos generados 18. . . . . . . . . . . ¾Qué sigue? . . . . . . . . . . . . . . . . . . 17. . . . Creando catálogos de traducciones JavaScript . . . . . . Integración con un sistema de autenticación 18.2. . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . .. . . . . . . . . . . . . . . . . . . . . . . . . . . . Especicando cadenas de traducción en código de plantillas 20. . . . . . 19. . .2. .. . . . . . . . . . . . . . . . . . . . .3. . . . . . . . .3. . La vista de redirección set_language . . . . . . . .4. . . . . . .1. .3. .. . . . . . . . Marcando cadenas como no-op 20. . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . 20. . . . . .Extender la Interfaz de Administración de Django 19.9. . . . . . . . . . Pluralización . .1. . 20. . . . Traducciones y JavaScript . . . . . . . . . La vista javascript_catalog . . . . . . . . . . . . . . . . . . . . . . . . revisión 789 del 9 de noviembre de 2009 . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .viii ÍNDICE GENERAL 17. . . . . . . . . . . .Internacionalización 20. . . . . . . . . . . 18. . . . . . . . . . . . . Pesonalizar las plantillas de la interfaz . . . . Middleware Common . . . . . .3. . . .6. .1. .1. . . . . . . . . . . . . .4. . . . . . . .5. . . . . . El Zen de la aplicación Admin . . . . . . . . . . . . . . . . . . Integración con aplicaciones web existentes 18. . Usando el catálogo de traducciones JavaScript 20. Parada Completa . . . . . . . . . . .1.4. . 20. . . . . . . . . . 19. . . . . . . . . . . . . . . . . . . . . . . . . 19. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7. . . .3. . . . . .3. . . . . . . . 17. . . . . . . . 20. . . . . . . . . .2. . . . . 18. . . . . . . . . . . Funciones estándar de traducción . . . . Usando traducciones en tus propios proyectos . . . . . . . . . . . . . . . . . . . 19. .4. 20. . . . . . . . Creando los archivos de mensajes . . . . .4. . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . Empleo de 193 193 193 194 194 195 195 196 196 inspectdb . . . . . . . . ¾Qué sigue? . . . . . . . . 19. . . . . . . . Traducción perezosa 20. . . . . . . . . . . . . . . . . . . . . Plantillas de modelos propios . . . . . . . . . . . . . . . . . . . .. . . . . . . . . .2. 199 200 200 200 200 200 201 201 203 203 205 205 20.8. . . 19. . . . . . . . . . . . . . . . 20. . . . . . . . editando . . . .1. . . . . . . . . . . . . . 17. . . . .9. . . . . . . . . . 20. . . . . Creando archivos de idioma . . .2. . . . . . . . . . . . . . . . . . . . . . . . 20. . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . .2. .7. . . . . . . . . . . . . .7. . . .4. . . . . . Especicando cadenas de traducción en código Python . . .5. . . . . . . . . . . . . . . . . . . . . . . .4. 19. . . . . . . . .1.1. . . ¾Qué sigue? . .2. . . . . . . . .1. . . . . . . . . . . 20. . . . . . . .5. . . .2. . . . . . . . . Middleware de GET condicional . .4. gettext . . . . . . . . . . .. . .2. . . . . . . . . . . . . . . . . . .1. . . . . .1. . . . . . . . .8. . . . . . . . . Especicar los back-ends de autenticación 18. . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Cómo descubre Django la preferencia de idioma . Usuarios conables . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . Middleware de transacción 17. . . . . . . . .6. 207 208 208 209 209 209 210 211 211 212 212 214 214 215 215 216 216 217 217 . . . . . . . . Middleware X-View . . . . . . . .2. . . . . .7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . Ejecutando en un Servidor Único . . . . . . . . . 22. . . . . . . . . . . . .3. . . . . . . . . .6. . . .6. . . . . . . . . . . . . Usa memcached siempre . . . . . . . . . . . . .1. . . . . Corriendo multiples instalaciones de Django en la misma instancia Apache 22. . . . . . . . . . .5. . . . . . . . . . . A. . . Escalamiento . . . . . . . . . . . . . .2. . . . .5. . . . . . . . . . . . . . 22. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementando Balance de Carga y Redundancia . . 22. . . . . . . . . . . . . . . . . . .10. . . . . . . . . . . . . . . . . . . 21. . . . . . . . . . . . . Cross-Site Request Forgery 21. . . . . . .Puesta en marcha de Django en un servidor 22.3. . . . . . . . . . . . .7. . . . . . . . . . . . . . . . . . . . . . . . .4.4. . . . 22. . . . . . . . . . . . . La solución 21. . . . 22.1. . . . . . . .7. . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . .4. . . . . . . . . . . . . . Manejando fallas de segmentación 22. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Casos de estudio A. . . . . .1. . . . . . . . . . . . . . . . . . . . Comenzando . . . No hay tal cosa como demasiada RAM . . . . . . . . . . . . . . . . . . . . . . . . 22. . . . 21. . . . . . . . . . . . 22. . . .3. . . . . . . . . . . Session Forging/Hijacking . . . . . . . La solución 21. . . . . . Conguración básica . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . ¾ 22. . . . . . . . . . . . ¾Qué sigue? . . . . 22. . . . . . . . . .5. . . . . . . . . . . . . . . . . . . .5. Únete a la Conversación . . . . . . . . . . . . ¾Cómo les fue? A. . . . . . . . . . . . . . . . . . . . . . . . . 22. . . . .3. . . . . . La solución 21. . . . . . . . . . . . . . . . .5. . . 22. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21. . . . . . . . . . . . . . . . . . . . . . . .4. . . . . La solución 21. . . . . . Inyección de cabeceras de email . . . . 22.2. . . . . . . . .5. . . FastCGI y lighttpd . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Yendo a lo grande . . . . . . . . . . . . . . . . . . . . . . . . . . . . Descripción de FastCGI 22. . . . . . . . . . . . . . . . . . .7. . . . . . . .5. . . .1. . . . . . . . . . .6. . . . . . . . .ÍNDICE GENERAL ix 21. . . . Sirviendo Django y archivos multimedia desde la misma instancia Apache . . . .4. 21. . . . . 22. . . . . . . Directory Traversal . . . . . . . .3. . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . Palabras nales sobre la seguridad 21. . . . . . Ejecutando un Servidor de Medios Separado .1. . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Qué sigue? . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . .5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Corriendo un servidor de desarrollo con mod_python 22. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . 22. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Manejo de errores . . . . . . . . . . . . . . . . . . . . . . . . . . Separando el Servidor de Bases de Datos 22. . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Nada Compartido . . . . . . . . . . . . . . Usa memcached . . . . . . . . . . . . . .3. . . . . . 22. . .1. . . . . .1. . 21.6. . revisión 789 del 9 de noviembre de 2009 . .4. . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . 22. . . . . . . . . . . . . . . . . . . . . . .3. .3. . . . . . . . . . .5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8. . . . . . . . . . . . . . . . . .Seguridad 21. . . . . . . . . . . Un nota sobre preferencias personales . .3. . Estructura de Equipo 245 245 246 247 247 247 249 249 A. .1. . Exposición de mensajes de error 21. . . La solución 219 219 219 220 221 221 222 222 223 223 224 224 224 225 225 225 226 21. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Elenco . . . . . . . . . . . . .4. . . . . Ajuste de Performance . . . . . . Usando Django con Apache y FastCGI . . . A. . . . . . . . . . . . . . A. . . . . . . . . . . . . 22. . . . . .1. . A. .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Implementación . . . . . . . A.4. . . .1. . . . . . 22. . . . . . . . . . . . . Ejecutando Django en un Proveedor de Hosting Compartido con Apache .6. . .6. . . . . . . . . . . . . . .2. . . . . . . . Usando Django con Apache y mod_python 227 227 228 229 229 230 231 231 232 232 232 232 233 234 235 236 236 237 237 237 239 239 242 242 242 242 242 243 243 22. . . . . . . . . . .1. . . . . . . . . . . . . La solución . . . . . . . . . . . . . . . Portando código existente .4. . Usando Django con FastCGI 22. . . . . . . . . . . . .5. Ejecutando tu Servidor FastCGI 22. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . El tema de la seguridad en la Web 21. . . . . . . . . . . . . . . . . . . . . . . Inyección de SQL . . . . . . . . . . . . . . . . . . . . . . Deshabilita Keep-Alive . . . .6. . . . . . . . . . .9. . . . . . . . .5. . . . . .5. . . . . . . . . . . . . . . . . . . . . . ¾Por qué Django? . . . . . . . . . . . . . . . .8. . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6. . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5. . . . 21. .6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . .7.1. . . . . . . . . . . . . . . Cross-Site Scripting (XSS) . . . . . . . . . . . . . . . . 22. . . . . . . . . . . . . . . . . . . . . . . 22. . . . . . . . . . . . . . . . . . .6. . . . . . . . .

. . . . . . . . B. . . . . . . . . . . . . . . . . . . . . . . . . . .15. .1. . . . . . . . . unique_for_month . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . Opciones de los Metadatos del Modelo .4. .1. . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . Managers . . . . . B. . . . . . . . . . . . . . . . . . . . Managers Personalizados B. . . . . .6. . . . . . . . . . . . . . . . . . . . . . . db_table B. . . . .7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . unique_together B. . . . . . .1. . . . . . . . . . . . . . . B. . . blank . .1. . . . . . . . . . .4. . . . . . . 251 251 252 252 252 252 252 252 252 252 253 254 254 254 254 254 254 254 254 255 255 255 255 255 255 255 255 256 256 256 257 257 257 257 257 257 257 257 257 258 258 258 258 258 260 261 261 262 262 262 262 263 263 263 263 264 264 266 B. . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . B. . .1. . . . . . . . . . . . . .5. . .4. . .9. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5. . . . . . . . . . . . . . . . .14. . . . . . . . . . . . . . . . . . get_latest_by B. . . . . . . . . . . . . . . B. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . .3. . . . . . .2. .19. . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B. . . . . . .1. . .4. . . . . . . B. . . . . . . . . . . . . . . . . . . . primary_key B. . . . .11. . URLField . . . . . . .2. . unique_for_year . . . . . . . . . . . . . . . . . . . . . . . . . . . . . help_text . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Opciones Universales de Campo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . B. .8. . . .8.17. . . .2.1. . . . . . . . . B. USStateField . . . . . . . B. .1.7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . radio_admin B. . . . . . .1. . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . B. . .22. . . .9. . . . . . . . . . . . . . . . . . . . . . . . . . B. . . . DateField . AutoField . . . . . . null B. . . . . . . B. . . . . . IPAddressField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . PositiveSmallIntegerField . . . . . . . .1. . . . . . . . . . . . . . . . . . . verbose_name . . . . . ImageField . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . .2. . . . . . IntegerField . . .1. . unique_for_date . . . . . . . . . . . . . . . . . . .2. . . . . . . .x ÍNDICE GENERAL B. . . . . . . . . . . . . B. . . . . . . . . . . . . . . CharField . . . B. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . TimeField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . SlugField B. . . . . . . . . . . . . . . . . . . default . . . . . . . . . . . . .2. . . . . . . .16. . . . . . . . . . . .1. . . . . . . . .6. . . . .2. . . . . . . . . Métodos de Modelo .5. . . . . . . . . . . B. . . . . . . . . .2. . . . . . B. . . . . . . . . .8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . .1. . . B. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6.21. . . . . . . . . . . . . . . . . . . . .23. . . . . . . . . . . . . . .24. . . B. . . .20. .10. . . . . . revisión 789 del 9 de noviembre de 2009 . . . . . . B. Campos . . . . . . . . . . . .5. . . . . . . . db_column . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TextField . . . choices .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . db_index B. B. . B. . . . . . B. . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . .2. FloatField . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . .11. . . . . . . . . . . FileField . . . . . . . . . B.1. . . SmallIntegerField . . . . . . . . . . . . . . . . . . . . . . . . . . . B. . . . . . . . . . . . . . . . Referencia de la denición de modelos B. . . . . . . .3. . . . . . .3. . . . . . B. . . . . . . . . . . . . . . .12. . . . . . . . . . . . . .6. B. . . . . . . . . . . .5. .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . PositiveIntegerField B. . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . unique . . . . . . . . . . . . . . . . . . . . . . . . . .1. FilePathField . . . . . . . . . . . . . . . . . NullBooleanField . . . . . . . . . . . . . . . . . . . . . . . . . B. . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . B. . . .1. . . . permissions . . . . . B. . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . .12. . . . . . . . . . . . .7. . . . .1. . . . . . . . .1. . . . . . . . . . . . . . . . . . .2. . . . . . . . CommaSeparatedIntegerField . . . . . . . . . . . . . . PhoneNumberField . . B. . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . .4. . . B. B. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13. . . . . . . . . . . . . B. . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . B. . . . . . B. . . . . . . . . . . . Relaciones . . . . . . . . . . . . . . . . . . . . B. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . B. . . . . . . . . . . . . . . . . B. . . . . . . . . . . . . . verbose_name B. . . . . . . . . . . . . . . . . . . . . DateTimeField B. . . . . . . . EmailField . . . . . . . . . . . . . . . . . . . . . . . . . . XMLField . . . ordering . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . .18. . . . . . . . . . . . . . . . . . . . . . . . . . . . .15. . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . Nombres de Manager . . . . . . . . . . . . . . . . .5. . . . . . . . . . . . . . .2. verbose_name_plural . . . .3. . .10. .2. . .4. . . . . . . . . . . . . . . . . . . . . . . . . . Relaciones Muchos-a-Muchos B. .1. . . BooleanField B. B. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .14. . . . . . . order_with_respect_to . Relaciones Muchos-a-Uno B. . . . . . editable . . . . . . . . . . . . . . . .

. . . contains . . . . . . . . . . . . .Métodos de Instancia Adicionales . . .2. . . . . . . . . . . . . . . . . B. C. . . . . . . . . . . . . . . . . . . gte. . . . . . . . . . C. ordering . . . . . . B. . . . . . . . . . . . in . . . .4. . . . . . .2. . . . . . .6. . . . . . . . . Creando Objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .8.7. . Ejecutando SQL personalizado . . . . . . . . . . Grabando cambios de objetos . .6. . . . . . . . . . . . . . . . . . . . .6. . . . . . . . . . . js . . . .6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . El patrón de búsqueda pk . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10. . . . . .6. . . B. . . . . . . . . . . . . . . . . Búsquedas complejas con Objetos Q C. . . . . . . . . . . . . . . . . . . . .6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7. . . . . . . . . . C. . . . . . . . . . . . . B. . . . . . . . . . . . . . . . . . . . . . . . .7. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . endswith and iendswith C. . . . . . . . . . . . . . . . . .7. . . . . . . . list_display . . . . . . . . .3. . . . . . .14. . . . Recuperando objetos . . . . and lte . . . . . . . . . . . . . . . . . Métodos de consulta que retornan nuevos QuerySets . . . . . C. . . . . . . . . . . . . . . . . . . . . . . . . . list_select_related . . iexact . . .6. . __str__ . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6. . . . . . . . . C. . . . . . . . . . . . . . . revisión 789 del 9 de noviembre de 2009 . . Filtrando objetos . . . . . . istartswith C. . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . C. . . . . . . . . . .6. . . . . . . . . . . . .7. . . . . . . . . . .6. . . . . . . . . . . . . . .6. . . . . . . . . . . . . .7. . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . C. . . . . . . . . . . . .5. . . . Encadenando ltros . . . . . . . . . . . . . . . . . . . C. .7. . . . . . . . . . . Limitando QuerySets . . . . . . . . . . . . . isnull . .7. . C. . . .6. . .4. . .2. . . . . . . . .12. . year. . . . . . . . . . . . . . . . . . . . . . . .9. . . . . . . . . . . . . .4. . . . . . . . icontains . . . . get_absolute_url . . . . . . . .2. . . . . . . . . . . . . .7. . . . .3. Objetos Relacionados . . C. . . . . . . . . . .5. . . . . . . . . . . . . . . . . .10. . . . .5. . . . . . . . . . . . . .6. . . . . . . . . . . B. . . . . startswith . . . . . . .12. .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6. . .9. . . . . . save_as . search . . Relaciones de Clave Foreánea Inversas C. . . . . . . . . . . . . . . . .10. . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C. . . . . . . . . . . . month. . . . . . list_display_links B. . . . . . . . . . . .7.1. . . . . . . Caching y QuerySets . . . . . . . . . . . . . . . . . Consultas Que Cruzan Relaciones C. . . .7. . . . . . . . . . . . . . . . .3. . . C. . . . . . . get_FOO_lename() C. . . . . . . . . . . .2. . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . Consultas que Abarcan Objetos Relacionados . . . . .6. . . . . . . . . . . . . . . . . C. B. . . . . . . . . . . . .13. . . . . . . . . . . . .2. . . . . . . . .4. . and day . . . . . . . . . . . . . . . . .8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Patrones de búsqueda C. . .6. . . . . . . . . . . . . . . . . . . . . C. . . . . C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . .6. . C. . . . . . . . . . . . . Referencia de la API de base de datos C. . . . . . . . . . . . . . . . . . . . list_per_page . . . . . . . . .7. . . . . . range C.8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . .5. . . . . . . . . . . . . . . . . . . C. . . .1. . . . . . . . . . . . . . lt. .11. . . . . . . . . . . . . . . . . . . . . . . . . . . C. . . . . . . . . . . . .6. .7. B. . . . . . . . . . . . . . get_next_by_FOO(**kwargs) y get_previous_by_FOO(**kwargs) . . . . . . . . B. . . . . . . . . . . . . .10. . . . . . . . . . . . . .10. . . C. . Relaciones muchos-a-muchos C. C. B. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5. . Qué pasa cuando grabas? 275 275 276 276 277 277 278 278 279 280 280 283 285 286 286 286 286 287 287 287 287 287 287 288 288 288 288 289 289 290 290 290 292 292 292 293 293 293 294 294 C.6. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C. . . . . . .3. . . . . . . . . . . . . . . . . . . . .ÍNDICE GENERAL xi B. C. . . . . . . . . . . . . . . . . get_FOO_url() . . . . . . . . . . . . . . . . elds . . . . . . . . . . . . . . . . . . . . .9.7. . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . B. . . . . .10. . . . . . . .6. . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . C. . . . . . . . . . . . . . . . . . . . . . . B. . . . . .11. . . . . . . . . . . . . . . . .7. . . . . . . . . . . . . save_on_top . . . . . .8. . . . . . . . . . . . . . . . . . . .5. . . . . . .5. . . . . . . . . . . . . . . . . . . . C. . . . gt. . . . . . . . . . . . . . . . . . . . . . . . . . . . . Relaciones de Clave Foránea C. . date_hierarchy . . . . . . . . . . . . . B. .5. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . get_FOO_display() C. . . . .8. . . . . . . . . . . . . . . . Opciones del Administrador . . . . . . . . . . . . . . . . . . . . . . . . . . .6. . .8. . . . B. . . . . . . . . . . . . . . . . . . . . . .7. . . . . . . . . . . . . . . . .1. . . . . . . Sobreescribiendo los Métodos por omisión del Modelo 266 267 268 268 268 269 269 270 270 271 272 272 272 272 272 272 272 B. . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . exact . . . . .1. . Borrando Objectos . . . . . . . . . . C. . . . . . . . . . . . .3. . . .10. . . . . . . . . . C. . . list_lter B. . . . . Metodos de QuerySet que no devuelven un QuerySet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . search_elds . . . . . C. . . . . . . . . . . . . . Claves primarias autoincrementales . . . . . . . . . . . .3. . . . . . . . . . . . . .8. . . . . . . . . . .

. . . . . . . .4. . . . . .3. . . DATABASE_OPTIONS . . . . . . . . . . . . DATABASE_HOST . . . . . . . . . .10. . . . . . . . . . . . raw_contents) . . . . . . . . . .Atajos (Shortcuts) . . . . . . . . . . . . . . . . . E. . . . . Argumentos comunes a todas las vistas genéricas . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . .14. . . . . . . . . . . . . .3. . . . . . . . .1. . . . . . . . . Referencia de las vistas genéricas D. . . . . . . . . . .6. Vista de creación de objetos .4. . . . . . . . . . . . . . . . . . . . . . . . . D. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Archivos diarios . . . . . . . . . . . . ADMIN_FOR ADMINS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Representar una plantilla 297 297 298 298 298 299 299 300 302 302 303 304 306 307 308 308 309 310 311 312 D. . . . .6. . . . get_FOO_height() and get_FOO_width() . Vistas genericas para Crear/Modicar/Borrar . . . . . . . . . . . . . . . . . . . . .3. E. C. . . Viendo cuáles variables de conguración has cambiado . . . . . . . . . . . . . . . . . . . D. . . . . . .5. . . . . . . . . . . . . . .4. . . . D. . . . . . .5. . . . . . . . .7. . . . . . . . . .9. . . . . . . . . . . .1. . . . . . . . D. . . . . . . . . Archivos mensuales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . E. . . . .4.4. . . . . . .11. . . . . . . . . . . . . . .3. . . D. . . . . . . . . . . . . . . . . . . Archivos anuales . . . . . . . .4. D. . . .11. . . . E. . . .2. . . . . . . . . D. . . . . . . . . . . . . . . . . Valores por omisión .4. . Páginas de detalle basadas en fecha . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E. . . . .2. . . . . . . . . . . . . . . .10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . .4. . . . . . . . . . . get_object_or_404() 294 294 294 294 294 295 295 C.1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7. . . . .1. . . . . . . . . . D. . . . . Archivos semanales . .2. . . . E. . D. . . . . . . D. . . . . . . . . . . . . . . . . E. . . . . . . . .1. . . . . . . .4. . . . . . . . . C. . . . . . .13. . .4. . . . . . . . . get_FOO_size() . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . . . . . . . . . . . Vistas de listado/detalle . . . . . . . . . . . . . . . . . . . . . . . . . . . .py 313 313 313 313 314 314 314 314 314 315 315 315 316 316 316 316 316 317 317 317 317 317 317 317 317 318 318 318 318 318 Seguridad . .5.2. . . . . . . . . . . . . . . . . . D. . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E. E. . . . . . . . . . . .5. . . . . . . . . . . . . . . . . . D. . . . ADMIN_MEDIA_PREFIX . . . .4. . . . . . . . . . . . . .1. . . . . . . . . . . . . . . .10. . .4. .4. . . . . . . En el servidor (mod_python) . . . . Qué es un archivo de conguración . . . .15. . . . . . . . . . .3. . . . .4. . . Variables de conguración por omisión personalizados .3. . . . . . . . . . . . . . E. . . . . . . . . . . . .1. . . . . E. . . . . . . . . . . . . . . . . . . .10. . . . . . . . . . . . . . . . . . . . Listas de objetos . . . .3. . . . C. . . C. . . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . .7. . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . . .4. . . . . . . . . . . . . . . .2. . . . . . . . . . E. . . . . . . . . .6. . . . . . DATABASE_NAME E. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . Vistas genéricas basadas en fechas D. . . . . . . . . . . .4. . . . . .1. . . . . . . . . . . . . DATABASE_USER . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . Vista de borrado de objetos . . . . . . .1. .5. . .12. . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . .3. . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . .11. . . . . .2. . save_FOO_le(lename. . . . . . . .11. . . . . . . . . . . . . . . . . . . . . . . . Vista de modicación de objetos . . . . . .4. . .1. . . . . . . . . . . . . C. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . . . . D. . . . . . . . . . . . . . . . . . . . . . .5. . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .Utilizando SQL Crudo . CACHE_MIDDLEWARE_KEY_PREFIX . . . . . . . . . . . .8. . . .2. . . . . . . . D. . Vistas genéricas simples . . . .2. . .3. . . . . . . . . DATABASE_PASSWORD E. . . . . . . . . . . . . . E. . . . . . Índice de archivo . . . . . . . ABSOLUTE_URL_OVERRIDES . . . . . . . . .5. . . . . . . . . . . . . . . . . . . . . . . . get_list_or_404() . . . . . . . . . . . . . . . .1. . . . . . . . . . revisión 789 del 9 de noviembre de 2009 . . . . Redirigir a otra URL . . D. . . . .4. . .4. . . . . . . . . . . . . . . .1. . . . . . . . . . . . .4.xii ÍNDICE GENERAL C. . Archivo para hoy . . . . . . . . . . Indicando la conguración: DJANGO_SETTINGS_MODULE . . . Vista de detalle . . . . . E. . . . .12. E. E. . . . . . . . . . . . . . DATABASE_PORT . . . . D. . . . . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . D. .2. . . . . . . .1. . . . . . . .6. . . . . .4. . . . . . . . . . . . . . . . . . E. . E. . . . . . E. . . . . . . . . . . . . . . . .4. . .4. . . . . . . . Variables de conguración disponibles APPEND_SLASH . . . . . .3. . . . . . .5. . . . . . . . . . . . . . . . Usando variables de conguración sin jar DJANGO_SETTINGS_MODULE Es necesario que uses congure() o DJANGO_SETTINGS_MODULE . . . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . Usando variables de conguración en código Python Modicando variables de conguración en tiempo de ejecución Creando tus propias variables de conguración La utilidad django-admin. . Variables de conguración E.4. . . . . . . . . . .1. . . . . . . .2. . . . . . . . . . . . . ALLOWED_INCLUDE_ROOTS CACHE_BACKEND DATABASE_ENGINE E. . . . . . . . . . . .

. . . .40. . . . . . .4. .63. . . . . . . . . . . . . . . . . . . . YEAR_MONTH_FORMAT . . . . . . . . . . . . . . MANAGERS . .4. . . . . . .24. . . . . . . . . . . . E. . . . . . . . . . . . . . E. . . . . . . . . . . . . . .16. . . . . . . . . . . . . . . . . . . . . . SESSION_COOKIE_SECURE E. . .4. .58. . SESSION_COOKIE_DOMAIN E. . . .17. . . DATETIME_FORMAT . . . . . . . . . . . . . . . . . . . . . . . . . . . .65.4. . .42.4. . . . . . . . . . . . . . . . . .31. . . . . . . . . . . . . . . . . . . .32. MIDDLEWARE_CLASSES . . .26. . . . . E. .4. . . . . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . .4. . . . . . . MONTH_DAY_FORMAT . . . . . . SERVER_EMAIL E. . . . . . . . . . . . . . . . . .36. . . . . . EMAIL_SUBJECT_PREFIX E. . . . . . . . . . . .27. . . . . . . .4. . . . . . . . .30. . . . . . . . . . . . . . . . . . . .4. . DATE_FORMAT . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . .29. . . . IGNORABLE_404_ENDS . .4. . .4.4. . JING_PATH . .4. . E. . . . . .4. . . . . . .4. . . . .54.62. . . . . . . .67. . . . . . . . LANGUAGE_CODE E. . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . .51. . . . . . . . . . . . . . . 318 318 318 319 319 319 319 319 319 319 319 319 320 320 320 320 320 320 320 320 321 321 321 321 321 321 322 322 322 322 322 322 322 322 322 323 323 323 323 323 323 323 323 324 324 324 324 324 324 324 325 325 E. E. . . . . . . .45. . . EMAIL_HOST . . . . SESSION_COOKIE_AGE . . . . . . . . . . . . . . . . . . . . . . E.4. . . . . . . . . . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . TEST_RUNNER E. .4. . . . . . . . . . . . . . . . . . . . . . . . . E. . . . . .19. . . . . . . . . . E. . . . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INSTALLED_APPS .66. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . .38. . .4. . . . . . .56. . . . . . . . . . . . FIXTURE_DIRS E. . . . TEST_DATABASE_NAME . . . . . . . . . . . .4. . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . E. . . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . E. . . . .4. . .4. . . . . . SITE_ID E. . . . . . . . . . . . . . . . . . . . . . . . . . . . .53. . . . . . . . . . . . . . . . . . . . . .43. . E.4.4. . . . . . . . . . . .4. . . . . . . . . . . .4. . . . . . . . . . . . . . . . .4. . . . . . .4. E. . . . . . . . .44. . . . . DEFAULT_FROM_EMAIL E. . . . . . . . . . . . . . . . . TEMPLATE_STRING_IF_INVALID E. . . . . . .4. . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . INTERNAL_IPS . . . . . . . . . . . . E. . . . . . . . . . . . . . . . . . . revisión 789 del 9 de noviembre de 2009 . . . . . . . . E. EMAIL_PORT . . . . . . . . . . . . . . . . . . . . . . . . . . TEMPLATE_LOADERS . . . . . . . . . . . . . . . . .28. . . . . . . .22. . . . . . . . . . . . . . . . . . . . . . . .41. . . . . . . . . . . . . . . . . . . URL_VALIDATOR_USER_AGENT . . . . . . . . . . . . . MEDIA_URL . . . . . . . . . . USE_ETAGS . . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . SECRET_KEY . . . . . . . . . . . .4. . . . . . . . . . . . . . . . . . . . . . .33. . . . . . . . . . . . . . . . . . . . . . E. . . . . .61. . . . . . . . . . . . . . . . . . . . . . . . . LANGUAGES E. . . . . . . . . . . . . . . . . . . . . . . . . . . .47. . .4. . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .50. . ROOT_URLCONF E.4. . . . . . . . . . . . . . . . . . . . . . . . . . .34. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TEMPLATE_DIRS . . . . . . . . . . . . . . . DEFAULT_CONTENT_TYPE E. . . . . . . . E. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TEMPLATE_CONTEXT_PROCESSORS . . .52. . . . . . . E. . . . . .4. . . . . . . . . . . . .4. . . MEDIA_ROOT E. .46. .4. . . . . . . . . . . . . . . . . . . . . . . .37. . . . . . . E. . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . .4. . . E. . . . . . . . . . . . .25. . . . . . . .21. . . . . . . . . . . . . .23. . . . . . . . . . . . . . . . . .18. . . . . . .59. . . . DISALLOWED_USER_AGENTS . . E. . . . . . . . . . . . .49. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .55.57. . . . . . . . . . . . . . . . . .64. . . .4. . . SERIALIZATION_MODULES . . . . . . . . . . . . . . DEFAULT_CHARSET . . . . . . . . . . . . . . . . . . . . . . . . . . . . EMAIL_HOST_USER . . . . . . . . . . . . . . . .4. . E. . . . .39. . . . .48. . USE_I18N . . . . TIME_ZONE . . . . . . . . . . . . . . . . . . . .ÍNDICE GENERAL xiii E. . . . . . . . . . . . . . .20. . . . . . . . . . . . . . . . SESSION_SAVE_EVERY_REQUEST . . . .4. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . TEMPLATE_DEBUG E. . .60. . . . . . . . . . . . . . . . . . . . . . . . . . . .4. . . . . . . . . . . . .4. . E.4. . . . . . . TIME_FORMAT E. . DEBUG . . . . . . . . . . . . . . . . . . . . . . . . . . . .35. . . SESSION_EXPIRE_AT_BROWSER_CLOSE E. . . SESSION_COOKIE_NAME . . . . . IGNORABLE_404_STARTS . . . PROFANITIES_LIST . EMAIL_HOST_PASSWORD E. . . . . . . . . . . . . . . . . SEND_BROKEN_LINK_EMAILS E. . . . . . . E. . . . . . . . PREPEND_WWW E. .

. . . . . .32. . . . . . . . . . . . . . . .10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . join . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . .2. . . . . . . random . . . . . . . . . . . . . . . . . . . . . . . . . .1. . .2. . . .7. . . . . . . .1. . . . . . . . . . . . . . . . .2. . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. .27. . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . removetags F. cycle . . . . . . . . . . . . . . . . . . . . . . . . . .29. . . . . . . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . .2. . . . . . . . . . . . . . . . . . escape . . . .2. . . . . . . linebreaks . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5. . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . .1. . . . . . . . F. . . . . . . . . . . . . . . F. . . . . . . . . . . . . . ljust . . . . .15. . . . . . . . . . . . . . . . . . . .3. . . . .1. . . . . . . . . . . . . . . . . . . . .25. . . . . . . . . . . . . . . . . . . . F. . . . . . . . . F. . . . . . . . . . . templatetag . . . . . . . ifequal . . . . . . . . . . . . . . . . . . . . . . . . . rst . . . . . . . . . . . . . . . . . . .9. . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . F. . . F. . . . . . . . . . . . . . . . . . . . . . . ifnotequal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . Etiquetas de plantilla y ltros predenidos F. . . . . . . . . . . . . . . . . . .8. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .1. . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .16. . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .13. . phone2numeric . . length_is F. . . . . . . . . . . . . . . . . . . . revisión 789 del 9 de noviembre de 2009 . . . . . . . . . . . . . . Etiquetas predenidas F.2. . . . . . . . . . . . . .16.13. .2. . . F. . . . . . . . . F. . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .23. . . . . . . . . . . . . . . . . . . . . . . . . . . .28. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . default . . . . . . . . . . . . . . . . . . . . . Filtros predenidos . . . . . . . . . . F. . . . . . . . . . . . . . . . . 327 327 327 327 327 328 328 328 328 328 329 330 330 330 331 331 331 333 333 334 334 334 335 335 335 335 335 335 335 336 336 336 336 336 336 336 337 337 337 337 337 338 338 338 338 338 338 338 338 339 339 339 339 339 340 340 340 340 lter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . block . . . . . . . . . . . . . . . . . . .1. . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . default_if_none dictsort . . . . . . . . . . .2. . . . . . . . . . . .2. . .3. . . . F. . . . . . . . . . . . . . . . . F. . . . . F. . . .18. . . . . .4. . .24. . . . .17. . . . . . . . . . . . . . . .1. . . . F. . . . .1. . . . . . . . . . .17. . . . . . . . . . . . . . . . . . . . . . .30. . . . . . . .1. . . . . . . ifchanged F. . . . . . . . . . . . . . . . . now . . . . . . . .1. . . .33. . . . . . . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . divisibleby . .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . .1. . . . F. . . . . . . . . . . . . .1. .1. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . oatformat F. . . . . . . . . . . . addslashes . . . . . . . . . get_digit F. . . . . . . . . . . . . . . .6. . . .2. . . . . . . . .22. . .2. . . . . . . . . x_ampersands F. . . . . . .14.34. . . . . . . . . . . spaceless . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . regroup F. . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. .2. . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . F. . . . . F. . . . . . . . . .2. . . . . . . . . . . . .2. . . caprst center date cut . . . . . . . . . . . . .2. . . . . . . . .1. . . . . . . . . . . linenumbers . . . . . . .9. . . . . . . . . . . . . make_list . . . . . .5. . . . . . . . . . . . F. .2. . . . . . . . .2. . . . . . .20. . .1. . . .8. F. . .19. . . . . . . . . . linebreaksbr . . . . . . . . . . . widthratio . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .10. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . .7. . . . .2. . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . .18. F. . . slugify . .2. . . . . . . . . . . . . F.2. . . . . . . . . . . . . . . . . . . .xiv ÍNDICE GENERAL F. . . . . . . . . . . . . . .12. . . . . . . . . url . . . pluralize . . . slice F. . . . . . . . . . . . . . . . . . . . .11. .2. . . . . . . . . . . . . . . . . . . . . . lower . . . . ssi F. . . . . . . . . . . . . . . . . . .12. .2. . . . . . . . . . . . . . . include . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .15. . . . . . . . F. . F. . . . . . . . . . . . . . . . . . . . F. . . . . lesizeformat F. . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . length . . . . . . . . F. .1. . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . F. . . .2. . . . . . . . . . . . . . . . . . . . . . . . . .20. . . . . . . .1. . F. . . . . . . . . . .2. . . . . .14. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .31. . . . . . . . . . . . . . . . . . . . . . . . . . . . add . . . . . . . . . . . . . . . .4. .1. . . . . . . . . . . . . . comment debug extends rstof . if . . .1. . . . . . . . . . . . . .2.6. . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . for . . . . . .19. . . . . . . .26. . . F.11. . dictsortreversed . . . . . . . . . . . . . . . . . . . . . rjust . . . . . . . . . . . . . . . . load F. F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .21. . . . . . . . . . . . . . . . . . . . . . . . . pprint . . . . . . .21. . .

. . . . . . . . . . . . . .] . . . .] . .. . . . . . . . . . . . . . . . . . . . . . . . . .2. .. . . . shell . . . . . . . . . . --help G. . . . . . . . . . . . . . . .2. sqlall [appname appname . . . . . G. . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . . . .35. . . . . . . . . . . . . . . . . . . . . . . . .9. . . . . . . . . . . . . . . . .2. . .4. . . . . . . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . .11. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . --version . . . urlize F. . .2. . . . . . . . . . . . . . . . . .. . . . F. . . . . . . . . . . .14. . . G. . .2. . . . .15. . . . . F. . . . . . . .1. . . . . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . .2. . . . . . . . . . . . . . . . . .41. .13. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .19. . . . . . . . . . . F.49. . . . . . . . . . . . . . . . . . . . . G. . . . . . . . . . . . . G. . . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . runfcgi [options] 345 345 345 346 346 346 346 346 346 346 347 348 348 348 348 349 349 349 349 349 349 349 349 349 349 350 350 350 350 350 350 350 350 351 351 351 351 351 G. . . . . . . . . . . . . . . .8. . . . . . . . . . . --noinput G. . . . . . . . . .20. . . . . G. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . G. .3. . . . . . . . . . . . .45. . . . . . . . . test . . . . . . . . . . . . . . . . . . . .23.2. . . . . . . G. . .. . . . . . . . G. . . . . . . . .. .2. . . . . . . timeuntil F. . . . . . . . . . adminindex [appname appname . . . . . . . . .2. . . . .. . . . . . . ..47. . sqlindexes [appname appname . . . . . . . . . . . . . .] G. . . . . . . . . . . . . . . . .3. . .] . . . . . . . . . . . . . . . . . . . . . . . . . . . --adminmedia . . . . . . . . . . . . . --pythonpath G. sqlclear [appname appname . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . .1.2. . . . . . . . . . . . . . . . . . . . . . . . . .2. . . . . . . . .. . . . . . . . . . . .2. . . . . . . . . .18. . . . .. . sqlcustom [appname appname . . . . .6. . . . . G. .8. . . . . validate G. . . . . . . . . . . . . . . . . . . . . . . . . . . . . sqlsequencereset [appname appname . . . . . . . . . . .38. . .2. . . . G. . . urlencode F. . . . . . . . . . . sqlreset [appname appname . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . . G. . wordwrap . . . .2.5. .3. . . . . . . . . . . . . . . . . . . disettings . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . . .6. . .] .3. . . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .46. . . . . . . . . . . . . . . . . . . . . . . . . . . . . ..] . . . . G. . . . . .44. . . . .. . . . . . . title . . . . . . . . . . . . . . . . dbshell . . . . . syncdb . . . . . .2. . runserver [número de puerto opcional. . Opciones Disponibles . . . . .. . . . . . . . . . . . . . truncatewords . . . . reset [appname appname . . . . . . . . . . .2. . .1. .. . . . .2. . startproject [projectname] . . . . . . . . . . . . . . . . . .7. . . . . . . . . . . . . . startapp [appname] . . . . .37. . .36. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .7. . . . . . .] . . . . . . . . . unordered_list F. . . . . . . . . . . .3.. . . . . . . . . . .2. . . . . . . . . striptags . . . . . . . . . . . . . . . . . . . . . . . . . --indent G.. . . . . . . . . 340 340 340 341 341 341 341 341 341 342 342 342 342 342 342 343 . . . . . . . . . . . . . . . . . . . . F. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . G. . . . dumpdata [appname appname . . . . . . F. . . . . G. .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .2. . loaddata [xture xture . . . . . . . . . . . . G. . . . . . .2. . . . . . . . . . . . . . . revisión 789 del 9 de noviembre de 2009 . . . . . . .2. . . . . Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .22. . . . . . . . . . urlizetrunc . . . upper F. . . . . . . .. . . .50. . . . . . . . . . . . . . G. . . . . . . . . . . yesno . . . . . G. . . .3. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .5.. . G. . . . . . . createcachetable [tablename] G.. . . . . . . . . . . . . . .ÍNDICE GENERAL xv F. . . . . . . . . F. . El utilitario django-admin G. . . . . . . . . . . . . . . .10. .48.] . . . . . . . . . . .2. . . . . . . . . . . .] .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . wordcount . . . G. . . . . . .. . . . . .42. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .9. . . . . . . . . . . . . . . . . . inspectdb . --noreload . . . . time . . . . . . . . . . . . . . . . . . . . . . . . . .12. . . . . . . . . G. . --settings G. . . . . . . .. . . . . . . . . . . . . . . . . G. . . . . . . . . . . . . . .3. . .2.. . . . . . . ush . . . . . . . sql [appname appname . . . . . .2. . . . . . . . . . . . . . . G. . . . . . . .] G. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . .39. . . . . . . .2. . or direcciónIP:puerto] G. . . . . . . . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .3. . . . . . . . . . . . . . . .10. . . . . . . . . . . . . . . . . . .2. . . .3.43. . . . . . . . .3. . . . . . . . . . . . . . . . .2. . timesince F. . .2. . . . . . . . . . G. . . . . . . . . . . --format . . .24.2. . . . . . . . . . . . . . . . . --verbosity . . . . . . . truncatewords_html . . . . . . . . . . . . . .2. . . . . . . . . Acciones Disponibles . .. . .21. . . . . . . . . . . . . . . . . .17. . . . . . . . . . . . G. . . .2. . . . . . . . . . .2. . . . . . . . . . . . . . . . . . G. . . . . . . . . . . . . . . . .4. . . . .] G. . . . . . . . . . . .16. . . . F. . . . . . . . . . . stringformat . . . . . . .2. . . . .2. . . . .40. . . . . . . . . . . . . . .

.2. . . . . . . . . . . . . . . . . . . . . . . . . . HttpResponse . . . . . . . Personalizar la Vista 404 (Not Found) . . . .5. . . . . . . . . .1. .1. .xvi ÍNDICE GENERAL H. . . . . . . . . . . . . . . . . . . . . . . H. .4. . . . . . . . . . . Un ejemplo completo . . . . . Personalizar la Vista 500 (Server Error) . . . . .6. . . . . . . .3. . . . . . . . . . . . . . . . Establecer las cabeceras . Subclases de HttpResponse . . . . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . H. .2. . . . . Objetos Petición y Respuesta H. . . . . . . . . . . . . . . . . . . . . . . . .1. . . . . . H. . . .2. . . . . . . . . Objetos QueryDict . . . . . H. . . . . HttpRequest . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . H. . . . . . . . . . . . . .2. . .2. . . . . . . . . . . . . . . . . . . . . . . . . . . . . H. . . . . .1. . . . . . . Docutils System Messages 1 revisión 789 del 9 de noviembre de 2009 . . H. . . . . .2. . . . . . H. . . . . . Retornar Errores . . . H. . . . . . . . . . . . .1. . Construcción de HttpResponses . . . . . . . . . . . . . . . . . . . .2. . . . . . . . . . . . . 353 353 355 356 357 357 358 358 358 359 360 A. . . . . . . . . . . . . . . . . .

Peter Bowyer. Reconocimientos El aspecto más graticante de trabajar con Django es la comunidad. James Mulholland. 1. Su ayuda y paciencia ha sido asombrosa. revisión 789 del 9 de noviembre de 2009 . Estamos especialmente agradecidos con aquellos que dispusieron de su tiempo para revisar el libro en profundidad y dejarnos decenas (a veces cientos) de comentarios: Marty Alchin. Justin Stockton.org. Brian Will y Joshua Works. Yasushi Masuda. Matt Drew. este libro no habría quedado terminado sin todo ese trabajo de su parte. En el Journal-World. uno de los mashups originales de Google Maps. Scott Lamb. por supuesto. este libro habría quedado en desorden. Es el fundador de EveryBlock. donde Django fue desarrollado. una plataforma de publicación online de noticias para compañías de medios de comunicación. Carlo Miron. Markus Majer. Muchas gracias a nuestro editor técnico. Brad Fults. Orestis Markou. Simon Greenhill. Nesta Campbell. Hemos sido especialmente afortunados de que Django haya atraído a tanta gente inteligente. Vadim Macagon. En su empleo diurno. Paul Bissex. Sobre los autores Adrian Holovaty. Daniel Tietze. Realmente apreciamos la ayuda y nos enorgullece que la excelente redacción de Simon pueda ser parte de este libro. Frankie Robertson. Graeme Stevenson. Oliver Beattie. Matt Boersma. Robert Dzikowski. Nick Eord. inexactitudes y código roto. Andreas Pfrengle. familias y compañeros que gentilmente toleraron nuestra ausencia mental mientras terminábamos este trabajo. Eduard Kucera. Kevin Teague. Jon Colverson. Queremos agradecer a todos y cada uno de ellos. Adrian hackea en proyectos de benecio público. Cuando no está trabajando en mejoras para Django. Michael O'Keefe. A Jacob se lo puede encontrar online en jacobian. Peter van Kampen. Un especial agradecimiento a Simon Willison por escribir el capítulo de procesamiento de formularios.2. Finalmente. Ludvig Ericson.D. Kansas. Robert Haveman. Nos sentimos realmente afortunados de que alguien con el talento de Jeremy encontrase el tiempo de ayudarnos. Anand Kumria. gracias a nuestros amigos. Mason. Max Battcher. Sin Jeremy. Alexandre Vassalotti.Capítulo 1 Preliminares 1. Kevin Menard. Jeremy Dunck. es uno de los creadores y desarrolladores del núcleo de Django. es el desarrollador principal para el Lawrence Journal-World. calidad y el ujo del libro nal. Casi mil personas dejaron comentarios que ayudaron a mejorar la claridad. Nos pone especialmente felices que Apress haya apoyado e incluso alentado el lanzamiento libre de este libro on line. es maravilloso ver a un editor tan abrazado al espíritu del open source. Sus revisiones y comentarios fueron indispensables. Alex Dong. Bob Stepno. Armin Ronacher. este libro no hubiese sido posible sin esa maravillosa revisión de pares. Jay Wang. Ross Shannon. Brooks Travis. Andrew Kember. Paul Smith. motivada y amistosa. Eric Floehr. R. Kent Johnson. supervisa el desarrollo de Ellington. R. Johan Samyn. con errores.com. desarrollador Web y periodista.1. Vive en Chicago y mantiene un weblog en holovaty. Je Croft. Robbin Bonthond.org. Carolina F. Marek Kubica. Peter Tripp. Lawrence Oluyede. Mike Robinson. David Grant. Silva. Un segmento de esa comunidad nos siguió durante el lanzamiento online beta de este libro. Chris Dary. Fredrik Lundh. Estamos agradecidos por todo el duro trabajo que la gente de Apress hizo en este libro. Daniel Roseman. Nielsen. Jacob Kaplan-Moss es uno de los principales desarrolladores de Django. Matthias Urlichs. como chicagocrime. Rod Begbie. Björn Stabell. una Web startup local de noticias. un periódico de dueños locales en Lawrence.

ar> César Ballardini <cesar en ballardini. revisión 789 del 9 de noviembre de 2009 . han contribuido de una u otra manera a este trabajo: Manuel Kaufmann <humitos en gmail.5.com> Alejandro Autalán <alejandroautalan en gmail. leeremos y utilizaremos estos comentarios en nuevas versiones.ar> Anthony Lenton <anthony en except. Peretti <federico en perettifederico. un sitio local personalizado con base en Dallas. Es uno de los primeros colaboradores de Greasemonkey y Django y ve la tecnología como una herramienta para la comunicación y el acceso al conocimiento.com> Ramiro Morales <ramiro+djbook en rmorales dot net> Juan Ignacio Rodríguez de León <euribates+django en gmail punto com> Percy Pérez Pinedo <percyp3 en gmail punto com> Tomás Casquero <tcasquero en gmail. El proyecto se lleva a cabo desde A la fecha.ar> César Roldán <cesar en hugoroldan. así que nosotros intentamos escribir una guía destacada que sirva además como referencia para Django.xviii CAPÍTULO 1.com.com.com> Marcos Agustín Lewis <marcoslewis en gmail punto com> Leónidas Hernán Olivera <lholivera en gmail punto com> Federico M. es que resulta que escribir libros sobre tecnología es particularmente difícil: sus palabras se vuelven anticuadas incluso antes de que el libro llegue a la imprenta. La primera es que amamos Django y queremos que sea tan accesible como sea posible.ar> Gonzalo Delgado <gonzalodel en gmail. Sobre los traductores http://humitos. Texas. publicado en Diciembre de 2007 por Apress con el título The Denitive Guide to Django: Web Development Done Right. La segunda. Hemos construido un sistema de comentarios que te dejará comentar sobre cualquier parte del libro.com. la tinta nunca se seca -podremos mantener este libro al día (y así lo haremos) --.com> Leonardo Gastón De Luca <lgdeluca84 en gmail. sin embargo.com> 1. PRELIMINARES 1.com> Renzo Carbonara <gnuk0001 en gmail. Sobre el editor técnico Jeremy Dunck es el principal desarrollador de Pegasus News. Sobre el libro Estás leyendo El libro de Django. En la web.com> Martín Gaitán <gaitan en gmail.homelinux.com> Guillermo Heizenreder <gheize en gmail.4. Muchos programadores aprenden su arte desde material técnico bien escrito. La respuesta de los lectores es una parte crítica de ese proceso. 1. La traducción al español de El libro de Django fue posible gracias a la colaboración voluntaria de la comunidad Django en Español y Python Argentina. Hemos lanzado este libro libremente por un par de razones.net/django-book.com.com> Milton Mazzarri <milmazz en gmail.3.

y decenas de lenguajes y entornos similares (ASP. Lo que es peor. CGI te permite pensar en páginas como recursos generados dinámicamente bajo demanda. Django está diseñado para hacer foco en la diversión. rápidamente se hizo obvio que esta situación era tediosa. en las partes interesantes de tu trabajo. JSP. consumía tiempo y al nal era insostenible. Hacemos cuanto podemos para leer todos los comentarios posteados allí y responder tantos como sea posible. etc. los desarrolladores web escribían cada una de las páginas a mano. dejando que trabajes fuera del alcance del framework cuando es necesario. por favor envíanos unas líneas (en inglés) a feedback@djangobook. Django intenta mantenerse fuera de tu camino. Con esta nueva explosión del desarrollo web comienza otro incremento en la ambición. CGI tiene sus problemas: los scripts CGI necesitan contener gran cantidad de código repetitivo que los hace difícil de reutilizar. ½nos encantaría escucharte! Nos alegra que estés aquí. Al hacerlo. La versión online de este libro te permite dejar un comentario en cualquier parte del libro y discutir con otros lectores. una por vez. y esperamos que encuentres a Django tan emocionante. Estamos extremadamente interesados en la retroalimentación. PHP hace poco para proteger a los programadores en cuanto a vulnerabilidades de seguridad. la curva de aprendizaje para algunos que recién conocen HTML es extremadamente llana. desarrollo y despliegue de sitios satisfactorios y de los cuales te sientas orgulloso. Actualizar un sitio web signicaba editar HTML. PHP solucionó varios de estos problemas y tomó al mundo por sorpresa --ahora es. sitios interesantes en un tiempo extremadamente corto. y esto cambió la web para siempre. por lo que muchos desarrolladores de PHP se encontraron con que tenían que aprender sobre seguridad cuando ya era demasiado tarde. la herramienta más popular usada para crear sitios web dinámicos. De cualquier modo. La mayor innovación de PHP es que es fácil de usar: el código PHP es simple de embeber en un HTML plano. Django te permite construir en profundidad. Escribimos este libro porque creemos rmemente que Django mejora el desarrollo web. fue desarrollado) solucionó este problema permitiendo que el servidor web invocara programas externos capaces de generar HTML dinámicamente. Ahora es duro imaginar la revelación que CGI debe haber sido: en vez de tratar con páginas HTML como simples archivos del disco. Como los sitios web crecieron y se hicieron más ambiciosos. Está diseñado para poner rápidamente en movimiento tu propio proyecto de Django. Sin embargo.Django y Ruby on Rails parecen ser muy populares en estos días -. Al mismo tiempo. Un grupo de emprendedores del NCSA (Centro Nacional de Aplicaciones para Supercomputadoras. Si preeres utilizar correo electrónico. Pero PHP tiene sus propios problemas. del T. un rediseño implicaba rehacer cada una de las páginas. así como complicados de entender y escribir para los desarrolladores novatos.) siguieron de cerca el diseño de PHP. El desarrollo de CGI hace pensar en la primera generación de página web dinámicas. por lejos. al mismo tiempo que alivia el dolor de las partes repetitivas. proporciona abstracciones de alto nivel de patrones comunes del desarrollo web. Django fue inventado para satisfacer esas nuevas ambiciones. o CGI 1 .: Common Gateway Interface revisión 789 del 9 de noviembre de 2009 . de forma dinámica. Estas y otras frustraciones similares. Estos frameworks -. por su facilidad de uso alienta a la producción de código mal hecho. donde Mosaic. 1 N. se espera que los desarrolladores web hagan más y más cada día. el primer navegador web gráco. Ellos llamaron a este protocolo Puerta de Enlace Común.Capítulo 2 Introducción Al comienzo.reconocen que la importancia de la web se ha intensicado en los últimos tiempos.com. divertido y útil como nosotros. en última instancia aprenderás todo lo que necesites saber para producir un diseño. condujeron directamente al desarrollo de los actuales frameworks de desarrollo web de tercera generación. atajos para tareas frecuentes de programación y claras convenciones sobre cómo resolver problemas.

.

¾Qué es un Framework Web? Django es un miembro importante de una nueva generación de frameworks Web. Al mismo tiempo. explicamos en profundidad lo que hace Django.1. ¾Y qué signica ese término exactamente? Para contestar esa pregunta. Utilizando Django puedes crear y mantener aplicaciones Web de alta calidad con un mínimo esfuerzo. contestando la pregunta ¾Cómo puedo aplicar estas herramientas de forma efectiva en mis propios proyectos? Al leer este libro. el desarrollo web es un acto entretenido y creativo. El objetivo de este libro es convertirte en un experto de Django. provee un alto nivel de abstracción de patrones comunes en el desarrollo Web. cuando escribías una aplicación CGI. Django intenta no entrometerse.fetchall(): print "<li> %s</li>" % fila[0] print "</ul>" revisión 789 del 9 de noviembre de 2009 . escrito en Python. hacías todo por ti mismo -. En esa época. atajos para tareas frecuentes de programación y convenciones claras sobre cómo solucionar problemas.el equivalente a hacer una torta desde cero --. passwd='dejame_entrar'. 3.el quid de tus aplicaciones Web -. que muestra los diez libros más recientemente publicados de una base de datos: #!/usr/bin/python import MySQLdb print print print print print print "Content-Type: text/html" "<html><head><title>Libros</title></head>" "<body>" "<h1>Los ultimos 10 libros</h1>" "<ul>" conexion = MySQLdb. Django te permite enfocarte en la parte divertida -. En este capítulo ofrecemos una visión general de Django.cursor() cursor. discutiremos conceptos de alto nivel cuando se considere apropiado. una forma popular de escribir aplicaciones Web alrededor del año 1998. aprenderás las habilidades necesarias para desarrollar sitios Web poderosos de forma rápida. Por ejemplo. dejándote trabajar fuera del ámbito del framework según sea necesario. puede ser una molestia repetitiva y frustrante. El enfoque es doble. db='mi_base') cursor = conexion. Segundo.al mismo tiempo que mitiga el esfuerzo de las partes repetitivas.execute("SELECT nombre FROM libros ORDER BY fecha_pub DESC LIMIT 10") for fila in cursor.Capítulo 3 Introducción a Django Este libro es sobre Django. en el peor. con código limpio y de fácil mantenimiento. En el mejor de los casos. y cómo crear aplicaciones Web con él.connect(user='yo'. consideremos el diseño de una aplicación Web escrita usando el estándar Common Gateway Interface (CGI). un framework de desarrollo Web que ahorra tiempo y hace que el desarrollo Web sea divertido. De esta forma. Primero. aquí hay un script CGI sencillo.

html'. Primero imprime una línea de Content-Type. También es sencillo de utilizar: tan sólo guarda este código en un archivo llamado Web y visita esa página con un navegador.order_by('-pub_date')[:10] return render_to_response('latest_books.py (la parte lógica) from django. seguido de una línea en blanco. se conecta a la base de datos y ejecuta una consulta que obtiene los diez libros más recientes.close() Este código es fácil de entender.db import models class Book(models.objects. Finalmente imprime el código para cerrar el HTML y cierra la conexión con la base de datos. INTRODUCCIÓN A DJANGO print "</body></html>" conexion. ¾Qué sucede cuando un diseñador Web que no tiene experiencia programando en Python desea rediseñar la página? Lo ideal sería que la lógica de la página -.cgi. de modo que el diseñador pueda hacer modicaciones sin afectar la búsqueda. este código es sencillo de comprender -. ¾Qué sucede cuando este código es reutilizado en múltiples entornos.py (las tablas de la base de datos) from django. de principio a n --.DateField() # views. para que puedas concentrarte en escribir código limpio y de fácil mantenimiento sin tener que reinventar la rueda. Estas tareas de conguración y cierre estarían mejor manejadas por una infraestructura común.shortcuts import render_to_response from models import Book def latest_books(request): book_list = Book. Un framework Web provee una infraestructura de programación para tus aplicaciones. Por un lado. el enfoque desde cero no es necesariamente malo. En resumidas cuentas. Pero a medida que una aplicación Web crece más allá de lo trivial. Precisamente estos son los problemas que un framework Web intenta resolver. se vuelve esencial alguna conguración especíca del entorno.2. No hay más nada que aprender. eso es lo que hace Django. Con una única página dinámica como esta.la búsqueda de libros en la base de datos -esté separada del código HTML de la página. ultimoslibros. sube ese archivo a un servidor 3. Así es como se podría escribir el código CGI anterior usando Django: # models. este enfoque se desmorona y te enfrentas a una serie de problemas: ¾Qué sucede cuando múltiples páginas necesitan conectarse a la base de datos? Seguro que ese código de conexión a la base de datos no debería estar duplicado en cada uno de los scripts CGI. {'book_list': book_list}) revisión 789 del 9 de noviembre de 2009 . cada uno con una base de datos y contraseñas diferentes? En ese punto. no hay más código para leer.2 CAPÍTULO 3. así que la forma pragmática de hacerlo sería refactorizarlo en una función compartida.CharField(maxlength=50) pub_date = models. Imprime HTML introductorio.incluso un desarrollador novato puede leer estas 16 líneas de Python y entender todo lo que hace.Model): name = models. tal como requiere CGI. ¾Debería un desarrollador realmente tener que preocuparse por imprimir la línea de Content-Type y acordarse de cerrar la conexión con la base de datos? Este tipo de código repetitivo reduce la productividad del programador e introduce la oportunidad para que se cometan errores. Hace un bucle sobre esos libros y genera una lista HTML desordenada. El patrón de diseño MVC Comencemos con un rápido ejemplo que demuestra la diferencia entre el enfoque anterior y el empleado al usar un framework Web.

3. la URL la denomina vista. de T. Un administrador de base de datos puede renombrar una tabla de la base de datos y especicar el cambio en un único lugar. Es útil entender por qué se creó el framework.urls. en lugar de tener que buscar y reemplazar en varios archivos.py (la configuración URL) from django.3.defaults import * import views urlpatterns = patterns(''. El camino clásico de un desarrollador Web es algo como esto: 1. deberíamos tomarnos un momento para explicar la historia de Django. Un diseñador puede cambiar el HTML de una página sin tener que tocar el código Python que la renderiza. probablemente estés familiarizado con los problemas del ejemplo CGI presentado con anterioridad. Si has estado creando aplicaciones Web por un tiempo. Tomadas en su conjunto. revisión 789 del 9 de noviembre de 2009 . ya que el conocimiento de la historia pone en contexto la razón por la cual Django trabaja de la forma en que lo hace.3. estas piezas se aproximan al patrón de diseño Modelo-Vista-Controlador (MVC). El Capítulo 5 profundiza también en la losofía MVC de Django. buscar. el Capítulo 3 trata sobre las vistas. El archivo /latest/ será manejada por la función latest_books(). En este libro. ) # latest_books. A esta función se urls. A esto se lo llama el modelo. un desarrollador puede cambiar la URL de cierta parte de la aplicación sin afectar la implementación subyacente. MVC dene una forma de desarrollar software en la que el código para denir y acceder a los datos (el modelo) está separado del pedido lógico de asignación de ruta (el controlador). como una clase Python. el Capítulo 4 sobre las plantillas. El archivo views.html (la plantilla) <html><head><title>Books</title></head> <body> <h1>Books</h1> <ul> { % for book in book_list %} <li>{{ book. LA HISTORIA DE DJANGO 3 # urls. Usando esta clase se pueden crear. Eso signica que cada pieza de la aplicación Web que funciona sobre Django tiene un único propósito clave. Lo que hay que notar principalmente en este caso son las cuestiones de separación : El archivo models. Por ejemplo. que puede ser modicado independientemente sin afectar las otras piezas. En este caso.html es una plantilla HTML que describe el diseño de la página. Por ejemplo. cada componente tiene su propio capítulo.py contiene una descripción de la tabla de la base de datos. y el Capítulo 5 sobre los modelos.py especica qué vista es llamada según el patrón URL.3.latest_books).: por loosely coupled ) entre sí. actualizar y borrar entradas de tu base de datos usando código Python sencillo en lugar de escribir declaraciones SQL repetitivas.name }}</li> { % endfor %} </ul> </body></html> Todavía no es necesario preocuparse por los detalles de cómo funciona esto -. views.tan sólo queremos que te acostumbres al diseño general --. Una ventaja clave de este enfoque es que los componentes tienen un acoplamiento débil (N.py contiene la lógica de la página. (r'latest/$'. Dicho de manera más fácil. que a su vez está separado de la interfaz del usuario (la vista).conf. La historia de Django Antes de continuar con más código. Escribir una aplicación Web desde cero. en la función latest_books(). El archivo latest_books.

Los apéndices son para referencia. La primera es el punto dulce de Django. luego de haber desarrollado este framework hasta el punto en que estaba haciendo funcionar la mayoría de los sitios World Online. Con eso en mente. Ellos forman los fundamentos de cómo se usa Django. son probablemente lo que releerás de vez en cuando para recordar la sintaxis o buscar un resumen rápido de lo que revisión 789 del 9 de noviembre de 2009 . tratamos de alcanzar un balance entre legibilidad y referencia.com que ofrecen información basada en bases de datos --.los periodistas (y los directivos) exigían que se agregaran nuevas características y que aplicaciones enteras se crearan a una velocidad vertiginosa. 5.com/.com/). no dejes que eso te quite las ganas -. Adrian Holovaty y Simon Willison.org y washingtonpost. Darse cuenta de que acaba de inventar un framework. 3. Debido a que Django nació en un entorno de noticias. craigslist. se comen su propia comida para perros).y con los que continúan encontrándose --. Los desarrolladores del framework tienen un alto grado de interés en asegurarse de que Django les ahorre tiempo a los desarrolladores. pueden ser leídos en cualquier orden. produzca aplicaciones que son fáciles de mantener y rindan bajo mucha carga.com. los desarrolladores originales de World Online todavía aportan una guía centralizada para el crecimiento del framework. Nació en el otoño boreal de 2003. comenzaron a usar Python para crear sus aplicaciones. ofrece varias características (en particular la interfaz admin. por el guitarrista de jazz Django Reinhardt. serás capaz de construir sitios Web que funcionan sobre Django. INTRODUCCIÓN A DJANGO 2. no puedes esperar enseñarle a alguien cómo hablar simplemente enseñándole el alfabeto).incluidos LJWorld. Los capítulos restantes.djangoproject. como se mencionó anteriormente. tratada en el Capítulo 6) que son particularmente apropiadas para sitios de contenido -. Cómo leer este libro Al escribir este libro. Escribir otra aplicación Web desde cero. Aunque existan otras razones.era la única forma en que podían crear aplicaciones mantenibles en tan poco tiempo -. El equipo de The World Online. 4.sitios como eBay. Así es precisamente como fue creado Django. Es así que Adrian y Simon desarrollaron por necesidad un framework de desarrollo Web que les ahorrara tiempo -. Debido a que Django fue extraído de código de la vida real. Django es activamente mejorado casi diariamente. Lo liberaron en julio de 2005 y lo llamaron Django. Refactorizar el código para que la aplicación 1 comparta código con la aplicación 2. los desarrolladores están motivados por sus propios deseos egoístas de ahorrarse tiempo a ellos mismos y disfrutar de sus trabajos. junto con la documentación libre en hacen ciertas partes de Django. y World Online colabora con otros aspectos importantes tales como tiempo de trabajo. prosperaban en un entorno de desarrollo dictado por las fechas límite del periodismo. Darse cuenta de que la aplicación del paso 1 tiene muchas cosas en común con la aplicación del paso 2. Lawrence. te recomendamos que leas los capítulos del 1 al 7 en orden.4 CAPÍTULO 3. que ahora incluía a Jacob Kaplan-Moss.djangoproject. La segunda cuestión a resaltar es cómo los orígenes de Django le han dado forma a la cultura de su comunidad de código abierto. 3. y creemos que la mejor manera de enseñar es a través de la prosa y numerosos ejemplos. Esta historia es relevante porque ayuda a explicar dos cuestiones clave. (Para decirlo sin vueltas. Kansas. Como resultado de eso. con una tendencia a la legibilidad.com -. una vez que los hayas leído.com y KUsports. 6. A pesar de que Django ahora es un proyecto de código abierto con colaboradores por todo el mundo.. materiales de marketing. Para los sitios -. es hacerte un experto en Django. y hosting/ancho de banda para el Web site del framework (http://www. decidió liberar el framework como software de código abierto. responsable de la producción y mantenimiento de varios sitios locales de noticias. en vez de proveer un exhaustivo pero inútil catálogo de las características de Django (Como alguien dijo una vez. está especialmente enfocado en resolver problemas de desarrollo Web con los que los desarrolladores de Django se han encontrado -. Django nació naturalmente de aplicaciones de la vida real escritas por un equipo de desarrolladores Web en Lawrence. en lugar de ser un ejercicio académico o un producto comercial. Nuestro objetivo con este libro. Existe una diferencia entre ser particularmente efectivo para algo y no ser efectivo para otras cosas). a menudo con sólo días u horas de preaviso. http://www. eso no signica que no sea una herramienta efectiva para crear cualquier tipo de sitio Web dinámico --. (De todas formas.4. cuando los programadores Web del diario Lawrence Journal- World. el equipo de World Online. En el verano boreal de 2005. Repetir los pasos 2-4 varias veces.a pesar de que Django es particularmente bueno para desarrollar esa clase de sitios. Ellos. los cuales se enfocan en características especícas de Django.

recomendamos leer el tutorial ocial de Python. Para ti.5. revisión 789 del 9 de noviembre de 2009 . es sólo cuestión de aprender a programar en Python y comprender cómo funcionan las bibliotecas Django. Django es sencillamente una colección de bibliotecas escritas en el lenguaje de programación Python. pero no es requisito para leer este libro. Actualizar este libro continuamente en el sitio Web en inglés.4. para que cualquier cosa que leas aquí todavía sea relevante en futuras versiones de Django. a continuación. En la lista de correo en inglés de usuarios de Django se juntan miles de usuarios para preguntar y responder dudas. revisa la versión más reciente de este libro en el sitio Web antes mencionado y también revisa la documentación ocial de Django.diveintopython.djangobook.google. el código Django no produce magia negra (es decir.org/tut/. ¾Qué sigue? En el Capítulo 2. te espera una grata sorpresa. variables.org/ y 3. Obteniendo ayuda Uno de los mayores benecios de Django es su comunidad de usuarios amable y servicial.desde instalación y diseño de aplicaciones. Si no tienes experiencia programando en Python. http://docs. tratamos de promover las mejores prácticas en desarrollo Web para los lectores a los que les falta este tipo de experiencia. Nuevas características de Django Tal como hicimos notar anteriormente. muy útil. También recomendamos Python.html el libro libre y gratuito de Mark Pilgrim Inmersión en publicado en inglés en papel por Apress. sí hace hincapié en las características y funcionalidades de Python cuando se considera apropiado. particularmente cuando el código no cobra sentido de inmediato. aprender Django será sólo cuestión de aprender las convenciones y APIs de Django.3. hashes/diccionarios). En conjunto. Conocimientos de programación requeridos turas de control (if. Suscríbete gratuitamente en (inglés) o http://www. hasta diseño de bases de datos e implementaciones -siéntete libre de hacer preguntas online. 3. Por ese motivo. estructuras de datos (listas.sourceforge.djangoproject. http://pyspanishdoc.1. La experiencia en desarrollo Web es. http://www.5. explicando su instalación y conguración inicial. Si quieres implementar con Django algo que no está explicado en este libro. nuestro objetivo como autores de este libro es doble: Asegurarnos que este libro sea a prueba de tiempo tanto como nos sea posible. Conocimientos de Python requeridos En esencia.4. Aprender Django. disponible en o su versión más reciente en inglés en net/tut/tut. A lo largo del mismo.características para cuando este libro sea publicado. ¾QUÉ SIGUE? 5 3.4.e incluso esenciales -. Aún así. y probablemente tendrá un gran número de nuevas -. trucos de programación cuya implementación es difícil de explicar o entender).python.4. entonces. Es fácil de aprender y muy divertido de usar. no deberías tener problema en meterte de lleno.com/r/django-users http://groups. disponible en http://es.4. Para desarrollar un sitio usando Django escribes código Python que utiliza esas bibliotecas.es/group/django-es (español). empezaremos con Django. Únete a la diversión en #django (inglés) o #django-es (español) en la red de IRC Freenode. Si tienes experiencia programando en Python. 3.2. Los lectores de este libro deben comprender las bases de la programación orientada a objetos e imperativa: estruc- while y for). para que puedas acceder a la mejor y más reciente documentación tan pronto como la escribimos. clases y objetos.com/. El canal de IRC de Django donde los usuarios de Django se juntan a chatear y se ayudan unos a otros en tiempo real. A pesar de que este libro no incluye un tutorial completo de Python.3. Para ayuda con cualquier aspecto de Django -. 3. como podrás esperar. Django es mejorado con frecuencia.

6 CAPÍTULO 3. INTRODUCCIÓN A DJANGO revisión 789 del 9 de noviembre de 2009 .

Si todo está funcionando .gz. incluyendo Puedes bajar 7-Zip de Cambia a algún django: bien.djangoproject.3 o superior. pero por ahora.tar.djangoproject. tienes que bajar e instalar Python.4.2.com/ estándar de instalación de Python.96. 3.1.Capítulo 4 Empezando Creemos que es mejor empezar con fuerza. Fíjate http://www.py install http://www. 4. Inc. confía en nosotros. "copyright".3 20030304 (Apple Computer.1 (#2. 4. En este capítulo explicamos las situaciones más comunes de instalación de Django.python. Django necesita Python 2. "credits" or "license" for more information. deberías poder importar el módulo >>> import django >>> django. 96. Instalar Python Django está escrito 100 % en puro código Python.gz cd Django-* sudo python setup. Si estás usando Linux o Mac OS X probablemente ya tienes Python instalado. build 1666)] on darwin Type "help". Instalar Django En esta sección explicamos dos opciones de instalación: instalar un lanzamiento ocial e instalar desde Subversion. 4. >>> en Si ves un error como: "command not found" u "orden no encontrada".2. así que necesitarás instalar Python en tu computadora.com/r/7zip/. Si Python 2. Escribe ves algo así. La mayoría de la gente querrá instalar el lanzamiento ocial más reciente de distutils http://www. 00:05:10) [GCC 3. Django se puede usar en cualquier sistema que corra Python. recomendamos usar 7-Zip para manejar archivos comprimidos de todo tipo.VERSION (0. este capítulo es divertido. que se llamará algo así como Django-0. que en el mundo de Linux es así: 1.1. Instalar Django es fácil. Baja el tarball. otro directorio e inicia python. En Windows. None) revisión 789 del 9 de noviembre de 2009 . El `Capítulo 20`_ explica cómo utilizar Django en producción.gz tar xzvf Django-*. Instalar un lanzamiento ocial Django usa el método download/. por eso es posible instalarlo de varias maneras. En los capítulos que le siguen a este descubrirás los detalles y el alcance del framework Django.tar. 2. Mar 31 2005.org/download/ para empezar. Python está instalado: python en una terminal. 4. La instalación es rápida y fácil.tar.

Haz un check out del trunk usando el comando djtrunk.mysql. Si sólo quieres comenzar a jugar con Django. determinará si el código ha cambiado. Asegúrate de tener un cliente de Subversion instalado. Es muy bueno.djangoproject. 2.org/) MySQL (http://www. querrás instalar una base de datos nalmente. Subversion contactará http:// code.com/r/python/site-module/.postgresql.djangoproject.2.a cada hora. Cuando ejecutes este comando.sqlite. sigue los siguientes pasos: 1. Incluye site-packages/django. Para iniciarlo sólo ejecuta el comando python en la línea de comandos. Crea agregando 4. aprender más de ellos en http://www. 4. EMPEZANDO Nota El intérprete interactivo de Python es un programa de línea de comandos que te permite escribir un programa Python de forma interactiva.org/) SQLite 3 (http://www. para esto necesitarás instalar un servidor de base de datos de algún tipo. Al último código de desarrollo de Django se hace referencia como el trunk. Todos los ejemplos de este libro asumen que tienes una base de datos revisión 789 del 9 de noviembre de 2009 .com/) un proyecto`_ -. Congurar la base de datos El único prerequisito de Django es una instalación funcionando de Python. salta a la sección  `Comenzando congurada. no necesitas ejecutar --½Acabas de hacer este trabajo a mano! Debido a que el trunk de Django cambia a menudo corrigiendo bugs y agregando funcionalidades.tigris.org/.pth y agrega el directorio djtrunk a este.py. y es el que el equipo de Django utiliza para administrar cambios en el código base de Django. conocido como un checkout local. en cualquier momento. probablemente quieras actualizarlo con frecuencia -. para obtener los últimos cambios y mejoras hechas por los desarrolladores de Django. es un sistema de control de versiones de código abierto similar a CVS. puedes djangoproject.pth son nuevos para ti. si eres un obsesivo. djtrunk/django/bin en el PATH django-admin. solo ejecuta el comando svn update desde el directorio djtrunk. Instalar Django desde Subversion Si quieres trabajar sobre la versión de desarrollo. deberías instalar Django desde el repositorio de Subversion. 4. y actualizará tu versión local del código con cualquier cambio que se haya hecho desde la última actualización. o si quieres contribuir con el código de Django en sí mismo.red-bean. Hasta el momento de escribir esto. Puedes conseguir este programa libremente desde http://subversion.com/. Puedes utilizar un cliente de Subversion para hacerte con el código fuente más actual de Django y.com.com/svn/django/trunk 3.pero créenos. python setup. El triple signo de mayor que (>>>) es el prompt de Python. Para obtener el trunk de Django. de tu sistema. Sin embargo. el desarrollo de sitios web con soporte de base de datos. para almacenar tus datos. puedes actualizar tu copia local del código de Django.py install Luego de descargarlo desde Subversion y haber seguido los pasos anteriores. Subversion es libre.8 CAPÍTULO 4. Este directorio incluye utilidades de administración como Consejo: Si los archivo . svn co http://code.3.2. El equipo de Django ejecuta sitios de producción sobre el trunk y procura permanecer estable. mostraremos ejemplos de código Python como si estuviesen escritos en el intérprete interactivo. Django admite tres motores de base de datos: PostgreSQL (http://www. y puedes encontrar documentación excelente en http: //svnbook. o actualiza tu PYTHONPATH djtrunk. Para actualizar el código. Durante todo este libro. este libro se centra en una de las mejores funcionalidades de Django.

py startproject mysite startproject creó: para crear el directorio mysite en el directorio revisión 789 del 9 de noviembre de 2009 . considera agregarlo a tu PATH. 4. Si estás trabajando con Python pysqlite 2.3.0. puedes encontrar los binarios precompilados de http: Si estás usando una versión de Python igual o posterior a 2. //www. y viene incluido en la biblioteca estándar de Python 2. obtener los drivers binarios de la base de datos es a veces un proceso complicado. Si esta es la primera vez que usas Django. usando un comando como sudo ln -s /path/to/django/bin/django-admin.djangoproject. incluyendo conguración de base de datos. El sitio web de Django siempre contendrá la última información acerca de las base de datos admitidas.4. En Unix. Como vas a utilizar con frecuencia django-admin. Nota django-admin.com/r/python-pgsql/windows/. la versión 3. En Windows.djangoproject.1.4. La compilación de drivers puede ser estresante. A nosotros el que más nos gusta es PostgreSQL. Usar Django con SQLite 3 Si estás usando PostgresSQL en Windows. puedes hacer un link simbólico de /usr/local/bin. Es por lejos el más fácil de congurar si sólo quieres jugar con Django. Ejecuta el comando actual. e ingresa a este directorio.5.com/r/python-sqlite/.3. 4.py. puedes encontrarlo en djtrunk/django/bin.3.py. Django actualmente no requiere una base de datos. 4. Sin embargo. necesitarás el paquete psycopg disponible en http://www.py /usr/local/bin/django-admin. necesitarás actualizar tu variable de entorno PATH .3.4. Si sólo quieres usar este como un servidor dinámico de páginas que no use una base de datos. Es un motor de base de datos extremadamente simple y no requiere ningún tipo de instalación y conguración del servidor.desde desde http://www. por razones que exceden el alcance de este libro.2.com/ psycopg en Toma nota de la versión que estás usando (1 ó 2). SQL perfectamente estándar.py debería estar en el PATH de tu sistema si instalaste Django con la utilidad setup. por lo tanto si eliges no usar una base de datos. Echemos un vistazo a lo que django-admin. tendrás que tener cuidado de algunas conguraciones iniciales.py.5 y el soporte incluido para SQLite. También necesitas instalar el paquete MySQLdb desde http://www.x no admite subconsultas anidadas ni algunas otras sentencias r/python-mysql/.djangoproject. En Windows. ya que están enlazados dentro de los binarios de pysqlite. por eso lo mencionamos primero. 4. recomendamos usar Python 2. opciones especícas de Django y conguraciones especícas de aplicaciones. 4.5. ten en cuenta que algunas de las herramientas extras de Django requieren una base de datos.djangoproject. Ya que sólo estás iniciándote con Django. necesitas SQLite 3 --no la versión 2-.0 o superior. Si estás utilizando PostgreSQL.3. por ejemplo algo como /home/username/djcode/. COMENZAR UN PROYECTO 9 Se está trabajando para admitir Microsoft SQL Server y Oracle. Si hiciste un check out desde Subversion. Asegúrate de tener pysqlite en una versión 2. ya tienes SQLite. todos los motores que listamos aquí trabajan bien con Django.3 o superior. (Señalaremos estas utilidades a lo largo del libro). Usar Django con MySQL Django requiere MySQL 4. Con esto dicho. En Windows. Usar Django con PostgreSQL r/python-pgsql/. está perfectamente bien.com/ Usar Django sin una base de datos Como mencionamos anteriormente. Crea un nuevo directorio para empezar a trabajar. necesitarás esta información luego. perderás estas utilidades. Comenzar un proyecto Un proyecto es una colección de conguraciones para una instancia de Django. SQLite merece especial atención como herramienta de desarrollo.4 o menor.4. puedes omitir la instalación separada de los binarios de SQLite.com/r/sqlite/ y el paquete http://www.djangoproject.

4. Lo siguiente: python manage. El servidor de desarrollo Django incluye un servidor web ligero que puedes usar mientras estás desarrollando tu sitio. Verás una página de Bienvenido a Django sombreada con un azul pastel agradable. ayudándote a hacer algunos cambios rápidos en tu proyecto sin necesidad de reiniciar nada. mira el `Capítulo 20`_ para información sobre cómo hacerlo con Django.py: Opciones/conguraciones para este proyecto de Django.0. desarrollar. el comando de línea de comandos: runserver inicia el servidor de desarrollo en el puerto 8000. porque al hacerlo se arriesga a que la gente sea capaz de ver el código en la web. Aunque el servidor de desarrollo es extremadamente útil para. Pon tu código en algún directorio fuera de la carpeta raíz. escuchando sólo conexiones locales. Con Django. settings. Verás Validating models. Django version 1. Apache) hasta que estés listo para la producción. urls.0:8080 hará que Django escuche sobre cualquier interfaz de red. resiste la tentación de usar este servidor en cualquier entorno parecido a producción. un grupo de módulos). Incluimos este servidor para que puedas desarrollar tu sitio rápidamente.py settings. probablemente pondrías el código debajo de la carpeta raíz del servidor web (en lugares como /var/www). using settings 'mysite. No es una buena idea poner cualquier código Python en la carpeta raíz del servidor web. 0 errors found. y no ha pasado por una auditoría de seguridad de ningún tipo. si aún no lo has hecho.0.1:8000/ con tu navegador web. ½Funciona! revisión 789 del 9 de noviembre de 2009 .1:8000/ Quit the server with CONTROL-C. sin tener que lidiar con conguraciones de servidores web de producción (i.e.py runserver 0. Entra en el directorio algo parecido a esto: mysite.0. bueno. una sitio hecho con Django.py: Un archivo requerido para que Python trate a este directorio como un paquete (i. Cuando sea el momento de lanzar tu sitio. Si quieres cambiar el puerto del servidor. Una utilidad de línea de comandos que te deja interactuar con este proyecto de Django de varias formas.0.py manage. El servidor de desarrollo puede manejar ablemente una sola petición a la vez. permitiendo que los demás equipos puedan conectarse al servidor de desarrollo.py urls. visita http://127. pasa este como un argumento python manage.py: La declaración de las URL para este proyecto de Django. EMPEZANDO mysite/ __init__.e. tabla de contenidos de tu ¾Dónde debería estar este directorio? Si vienes de PHP. Esto es utilizado especialmente si quieres compartir el desarrollo de un sitio con otros desarrolladores.1. y ejecuta el comando python manage.settings' Development server is running at http://127.py runserver.py runserver 8080 También puedes cambiar las direcciones de IP que escucha el servidor.py Estos archivos son los siguientes: __init__.. Cambiar el host o el puerto Por defecto.. no tienes que hacer esto. Este servidor de desarrollo vigila tu código a la espera de cambios y se reinicia automáticamente. Ahora que el servidor está corriendo..10 CAPÍTULO 4. 4.0. Esto no es bueno para la seguridad.py: manage.0.0.

revisión 789 del 9 de noviembre de 2009 .4.5. Duplicate explicit target name: próximo capítulo. en el de código básico que muestra cómo servir páginas Web usando Django. ¾QUÉ SIGUE? 11 4.5. ¾Qué sigue? `próximo capítulo`_ escribirás algo Ahora que tienes todo instalado y el servidor de desarrollo corriendo.

EMPEZANDO revisión 789 del 9 de noviembre de 2009 .12 CAPÍTULO 4.

o en realidad. explicamos cómo crear un proyecto en Django y cómo poner en marcha el servidor de desarrollo de Django. denimos la función llamada el nombre función de vista toma como primer argumento un objeto request.now() html = "<html><body>It is now %s. Nota que el nombre de la función de vista no importa. sólo muestra la salida del reloj interno del servidor.</body></html>" % now return HttpResponse(html) Repasemos el código anterior línea a línea: Primero. La vista en sí contiene toda la lógica necesaria para retornar esa respuesta. datetime de la biblioteca estándar de Python. views. el conjunto de módulos Luego importamos el módulo útiles que vienen con Python.http import HttpResponse import datetime def current_datetime(request): now = datetime.http. es una simple función de Python que toma como argumento una petición Web y retorna una respuesta Web. incluyendo una función que retorna la hora actual. Este es un buen ejemplo de una página dinámica. importamos la clase detalles de los objetos HttpResponse. 5. La llamamos claramente lo que hace. revisión 789 del 9 de noviembre de 2009 . Esta respuesta puede ser el contenido HTML de la página web. pero se podría llamar repugnante. Este simple ejemplo no involucra una base de datos cualquier tipo de entrada del usuario. una redirección. Este capítulo presenta cómo crear paginas web dinámicas con Django. porque el contenido de la misma no es estático -. Una función de vista. no tiene que ser nombrada de una determinada manera para que Django la reconozca. creemos un archivo llamado directorio mysite.al contrario. o vista en pocas palabras. La primera línea de código dentro de la función calcula la fecha/hora actual. El código puede encontrarse donde quieras. como un objeto y almacena el resultado en la variable local now..py en el el cual creamos en el capítulo anterior. El módulo datetime contiene varias funciones y clases para trabajar con fechas y horas. mientras que se encuentre dentro de tu Python path. los contenidos cambian de acuerdo con el resultado de un cálculo (en este caso. Para crear esta página. Cada HttpRequest. porque el nombre indica super_duper_awesome_current_time. Tu primera Vista: Contenido dinámico Lo primero que haremos es crear una página web que muestre la fecha y la hora actual. la cual pertenece al módulo django.datetime. o un error 404. Por poner el código en algún lugar. o un documento XML. el cálculo de la hora actual).1. No hay otro requerimiento -. el sitio no hace nada útil todavía -. o una imagen. o algo más datetime.datetime. como un documento HTML: from django.no hay magia. A continuación. crearemos una función de vista. Por supuesto. Cambiemos eso. La siguiente sección explica cómo Django encuentra esta funcion. cualquier cosa. al que típicamente se le asigna current_datetime aquí. Esta es la vista que retorna la fecha y hora actual.Capítulo 5 Los principios de las páginas Web dinámicas En el capítulo anterior.sólo muestra el mensaje It worked!. Para ver más HttpRequest y HttpResponse puedes consultar el Apéndice H. current_datetime.. por así decirlo. A Django no le interesa. Esta es una función de vista.

pero lo haremos más Zona Horaria de Django Django incluye una opción detalles.. se verá como: creó automáticamente una from django. incluyendo revisión 789 del 9 de noviembre de 2009 . LOS PRINCIPIOS DE LAS PÁGINAS WEB DINÁMICAS La segunda línea de código dentro de la función construye la respuesta HTML usando el formato de cadena de caracteres de Python. Generalmente no tienes que preocuparte de asigarle valores al Python path -. el HTML es inválido.urls. '/home/username/djcode/']. vacía. Finalmente. URLconf por tí: el archivo django-admin. Python va a buscar el módulo en import de Python. Básicamente.foo. Para esta URL. Veáse el Apéndice E para más 5. Si ejecutas el código Python from foo import bar. entonces probará si ese archivo no existe.conf. Cada función HttpResponse. Por omisión. llama a este código. # Example: # (r'^mysite/'. es donde vivas.foo')). Editemos ese archivo. abre un interprete interactivo de Python y import sys. '/usr/lib/python2. Una URLconf es como una tabla de contenido para tu sitio web hecho con Django. Python Path Python path es la lista de directorios en tu sistema en donde Python buscará cuando uses la sentencia Por supongamos que tu Python path tiene el valor [''. signica el directorio actual. signica Reemplaza el %s por el valor de la variable now.py).py en el directorio actual.py.) Si ese archivo no existe.py. y el signo porcentaje después de la cadena de caracteres. una cadena de caracteres ejemplo. Probablemente no settings.14 CAPÍTULO 5.py. es un mapeo entre los patrones URL y las funciones de vista que deben ser llamadas por esos patrones URL. Python lanzará Si estás interesado en ver el valor de tu Python path.path. (La primera entrada en el Python path. ImportError en escribe Si ese archivo no existe.defaults. establecer el Python path es una de las cosas que hace el archivo Cuando ejecutaste manage. /usr/lib/python2. Recuerda que estas funciones de vista deben estar en tu Python path. # ) # Uncomment this for admin: (r'^admin/'.admin.defaults import * urlpatterns = patterns(''.urls. seguido de print sys. la vista retorna un objeto adelante) de vista es responsable de retornar un objeto HttpResponse que contiene la respuesta generada.urls')). Es como decirle a Django.2. el script urls. /home/username/djcode/foo.4/site-packages/foo.4/site-packages'.py.conf.Python y Django se encargan automáticamente de hacer esas cosas por ti entre bastidores. django. y para esta URL. Python en primer lugar va a buscar el módulo llamado foo. include('django. Mapeando URLs a Vistas Repasemos.py startproject en el capítulo anterior. (Hay excepciones. El %s dentro de la cadena de caracteres es un marcador de posición.apps. Repasemos el código anterior línea a línea: La primera línea importa todos los objetos desde el módulo una función llamada patterns. include('mysite. Ahí es donde vienen las URLconfs. (Sí.contrib. por lo que puedes cambiarlo en tu TIME_ZONE que por omisión es America/Chicago. pero estamos tratando de mantener el ejemplo simple y corto) Por último. (Si eres curioso. ¾Pero cómo le decimos a Django que utilice ese código?.urls. llama a este otro código. esta función de vista retorna un página HTML que contiene la fecha y hora actual.

(Veáse el Apéndice E para una buena lectura sobre este tema). pasamos la función de vista current_datetime como un objeto sin llamar a la función.0. Por lo tanto. Nada más y nada menos. Deberías ver la salida de tu vista de Django. entonces revisión 789 del 9 de noviembre de 2009 . Puedes excluir la barra al comienzo de la expresión '^time/$' para que coincida con /time/. Esto es manejado como esperarías APPEND_SLASH tenga asignado el valor True. ½Qué bueno!. muestra ese mensaje). la cual Django espera encontrar en tu módulo Esta variable dene el mapeo entre las URLs y el código que manejan esas URLs. y el signo de dólar signica que exige que el patrón concuerde con el n de la cadena. usamos tanto el acento circunejo como el signo de dólar para asegurarnos que sólo la URL /time/ concuerde. El resto de las líneas están comentadas.es una tupla de Python en dónde el primer elemento es una expresión regular simple y el segundo elemento es la función de vista que usa para ese patrón. pero una URLconf puede ser incluida en otra URLconf. Este concepto se explica mejor con un ejemplo. Luego. (r'^time/$'. agregamos la línea (r'^time/$'. Django automáticamente agrega una barra antes de toda expresión.1:8000/. ) Hicimos dos cambios aquí.. y el dejar la barra de lado simplica mucho las cosas.py runserver. Para probar nuestro cambios en la URLconf.defaults import * from mysite. importamos la vista que en la sintaxis de import de Python se traduce a current_datetime).0. MAPEANDO URLS A VISTAS 15 La segunda línea llama a la función urlpatterns.la cadena de caracteres vacía. mysite.0. lo cual signica que puedes pasarlas como cualquier otra variable.0. todo lo que está en URLconf está comentado -. http://127. /time será manejada por la Esta línea hace referencia a un URLpattern -. entonces cualquier URL que comience con /time/foo como circunejo inicial ('time/$'). ejecutando el comando python manage.1:8000/time/.urls. A primera vista esto parece raro.2. así no tienes que reiniciar el servidor al hacer cambios). time/ '^time/' time/. El servidor está corriendo en la dirección abre tu navegador web y ve a ½Enhorabuena! Has creado tu primera página Web hecha con Django.views). por lo tanto. El acento circunejo signica que requiere que el patrón concuerde con el inicio de la cadena de caracteres.tu aplicación de Django es una pizarra blanca. Si hubiéramos utilizado el patrón el signo de dólar al nal). El caracter acento circunejo (^) y el carácter signo de dólar ($) son importantes. (La cadena de caracteres puede ser usada para proveer un prejo común para las funciones de vista. La función patterns() patterns() y guarda el resultado en una variable llamada sólo recibe un argumento -. En pocas palabras. Esto permite que las expresiones regulares sean escritas sin demasiadas sentencias de escape. en este ejemplo. Lo principal que debemos notar aquí es la variable ROOT_URLCONF. Django asume que acabas de crear el proyecto.views import current_datetime urlpatterns = patterns(''. Por defecto. está bien también. Esto es una característica de Python (y otros lenguajes dinámicos): las funciones son objetos de primera clase. current_datetime). función de vista current_datetime desde el módulo (mysite/views. como hiciste en el Capítulo 2. le estamos diciendo a Django que cualquier petición a la URL current_datetime. no sólo /time/. Django concordaría con cualquier URL que termine con y /time/bar. urlpatterns. Editemos este archivo para exponer nuestra vista current_datetime: from django. El servidor de http://127. así como así Del mismo modo. Primero. pero dejemos este uso más avanzado para más adelante). esta es la forma en la que Django sabía que debía mostrar la página It worked! en el capítulo anterior. inicia el servidor de desarrollo de Django. (Si ya lo tenías corriendo.py.conf. si dejamos de lado el carácter acento /foo/bar/time/. Algunas cosas que vale la pena resaltar: Notemos que. Esto se retoma en el Capítulo 8. ¾no? La r en r'^time/$' signica que '^time/$' es una cadena de caracteres en crudo de Python. Quizás te preguntes qué pasa si alguien intenta acceder a (a través de un redireccionamiento) siempre y cuando /time. Si la URLconf esta vacía.5. (Como nota adicional. desarrollo automáticamente detecta los cambios en tu código de Python y recarga de ser necesario. (sin concordaría.

mira el módulo http://www.digamos. realmente -. ROOT_URLCONF. Conoces ahora lo básico sobre cómo hacer páginas Web con Django. revisión 789 del 9 de noviembre de 2009 .resolución de URLconf a una función de vista que retorna un circuitado o HttpResponse-- puede ser corto- *augmented* mediante middleware. (punto) \d [A-Z] [a-z] [A-Za-z] + [^/]+ * {1.3. a-z (minúsculas) Cualquier carácter.3} Coincide con Cualquier carácter Cualquier dígito Cualquier carácter. Los secretos del middleware serán tratados en profundidad en el Capítulo 15. llama a la función de vista asociada con ese patrón. Django nos provee un poco más de exibilidad en el procesamiento de peticiones.py runserver importa un archivo llamado settings. El comando python manage. El ujo típico -. La función de vista es responsable de retornar un objeto HttpRequest como primer parámetro de la función. Cero o más ocurrencias de la expresión anterior (ejemplo. probablemente en la práctica no utilices más que un par de patrones regex. pero una de las conguraciones más importantes es ROOT_URLCONF Web.1. 5. A-Z (mayúsculas) Cualquier carácter. Aunque las URLconfs de Django permiten el uso de regexes arbitrarias para tener un potente sistema de denición de URLs. 5.py startproject creó el archivo settings. pasando un objeto luego). Este es el detalle de lo que sucede cuando ejecutas el servidor de desarrollo de Django y hacemos una petición a una página Web.sólo tenemos que escribir funciones de vista y relacionarlas con URLs mediante URLconfs. Cómo procesa una petición Django: Detalles completos Además del mapeo directo de URLs con funciones vista que acabamos de describir.py y urls. una petición a la URL apuntada por la variable ROOT_URLCONF. a-z (no distingue entre mayúscula y minúscula) Una o más ocurrencias de la expresión anterior (ejemplo. pero un esquema (ver Figura 3-1) te ayudará conceptualmente a poner todas las piezas juntas. Cuando encuentra uno que coincide.py? Bueno.Django carga la URLconf automáticamente. Esta es una pequeña selección de patrones comunes: Símbolo . \d+ coincidirá con uno o más dígitos) \d* coincidirá con cero o más dígitos) Entre una y tres (inclusive) ocurrencias de la expresión anterior djangoproject. LOS PRINCIPIOS DE LAS PÁGINAS WEB DINÁMICAS Expresiones Regulares Las Expresiones Regulares (o regexes ) son la forma compacta de especicar patrones en un texto.py desde el misLa variable mo directorio. comparando la URL solicitada con un patrón a la vez. ½Qué conveniente! Cuando llega una petición-. Este archivo contiene todo tipo de conguraciones opcionales para esta instancia de Django en particular. Cómo procesa una petición Django Debemos señalar varias cosas en lo que hemos visto. ROOT_URLCONF que apunta al urls. Luego comprueba cada uno de los patrones de URL en la URLconf en orden.com/r/python/re-module/. Es muy sencillo. Podrías pensar que es lento enlazar las URL con funciones usando una serie de expresiones regulares. le dice a Django qué módulo de Python debería usar para la URLconf de este sitio ¾Recuerdas cuando el settings.16 CAPÍTULO 5. pero te sorprenderás.py generado automáticamente tenía un django-admin.py generado /time/ -. (Veremos más de HttpRequest HttpResponse.3. Para más información acerca de las expresiones regulares. hasta que encuentra uno que coincida. Todos los caracteres excepto la barra.

1: El ujo completo de un petición y una respuesta Django.5. revisión 789 del 9 de noviembre de 2009 . CÓMO PROCESA UNA PETICIÓN DJANGO 17 Figura 5.3.

Si este middleware no retorna un HttpResponse. Eso es el acoplamiento débil en acción. mover desde /time a /currenttime/ -.4. Finalmente. Si una función de vista lanza una excepción. 5. el acoplamiento débil es una manera de diseñar software aprovechando el valor de la importancia de que se puedan cambiar las piezas. URLconfs y el acoplamiento débil Ahora es el momento de resaltar una parte clave de losofía detrás de las URLconf y detrás de Django en general: el principio de acoplamiento débil (loose coupling ). hemos denido un solo patrón URL: el que maneja la petición para la URL ¾Qué pasaría si se solicita una URL diferente? http://127.alterando la lógica de alguna manera -. y si el proyecto no lo estuviese. Estos tipos de middleware son *augmenting* los objetos HttpRequest así como también para proveer manejo especial a determinados tipos de peticiones. Esto permite el desarrollo de una pieza sin afectar a la otra. si quisiéramos exponer la funcionalidad de fecha actual en varias URL podríamos hacerlo editando el URLconf con cuidado. por ejemplo. nos dice también. no todo está perdido.18 CAPÍTULO 5. se retornaría una respuesta diferente. Las URLconfs de Django son un claro ejemplo de este principio en la práctica. consideremos la función de vista que escribimos antes. Deberías ver el mensaje Page not found (ver la Figura 3-2). si quieres cambiar la función de vista -. 5. la URL de tu aplicación es designada por dónde colocas el código en el sistema En contraste. el administrador Web. Por ejemplo.puedes hacer un rápido cambio en la URLconf. y la implementación de la función misma. Además.1:8000/hello/ o http://127. la denición de la URL y la función de vista que se llamará están débilmente acopladas. En una aplicación Web de Django. Explicaremos cómo desactivar este modo más adelante. Django incluye vistas por omisión para respuestas amigables a errores 404 y 500.org/) la URL de tu aplicación correspondía al nombre del método donde residía tu código. Si esto fuera un sitio en producción alojado en Internet.0. Con esa información. la cuál nos mostraba la fecha y la hora actual.0. Para averiguarlo.0. de esta manera. la página Page not found es sólo mostrada si nuestro proyecto en Django está en modo de depuración (debug mode ). sin tener que tocar una sola línea de código de la vista. residen en dos lugares separados. Por esta razón.5. pero puede tornarse inmanejable a largo plazo. En las URLconf anteriores. Errores 404 /time. el middleware de respuesta es bueno para el procesamiento posterior a un HttpResponse justo antes de que se envíe al navegador o haciendo una limpieza de recursos especícos a una petición. no quisiéramos mostrar esta información al público. o mejor como http://127.php. Por ahora.digamos. qué URLconf utilizó Django y todos los patrones de esa URLconf. LOS PRINCIPIOS DE LAS PÁGINAS WEB DINÁMICAS HttpRequest. sin preocuparte acerca de la implementación subyacente de la función. tendríamos que ser capaces de establecer porqué la URL solicitada lanzó un error 404. pero el middleware de excepción ayuda a aplastarlos. (http://www.0. el control pasa al middleware de Excepción.0. sólo diremos que todos los proyectos están en modo de depuración cuando los creamos. En las típicas aplicaciones PHP de archivos.cherrypy.1:8000/does-not-exist/. otras plataformas de desarrollo Web acoplan la URL con el programa. un manejador especíco a cada servidor construye la para pasarla a los componentes y maneja el ujo del procesamiento de la respuesta. la decisión de cuál debe ser la URL para una función. Esto puede parecer un atajo conveniente en el corto plazo. la excepción se vuelve a lanzar. Hasta a los mejores programadores se le escapan errores (bugs ). esta es información importante sólo destinada a ti. Continuaremos exponiendo ejemplos de esta importante losofía de desarrollo a lo largo del libro. (Es linda.net/). Para explicarlo simplemente. útiles para Cuando llega una petición HTTP desde el navegador. Si dos piezas de código están débilmente acopladas (loosely coupled ) los cambios realizados sobre una de dichas piezas va a tener poco o ningún efecto sobre la otra. Si quieres cambiar la URL de tu aplicación -. Sin embargo.puedes hacerlo sin afectar la URL a la que está asociada tu función de vista. En el caso de que alguno de los mismos retornara un HttpResponse la vista no es invocada. ¾no? A la gente de Django seguro le gustan los colores pasteles). Naturalmente. El manejador luego llama a cualquier middleware de Petición o Vista disponible.1:8000/ (la raíz del sitio). Similarmente.0. Django muestra este mensaje porque solicitaste una URL que no está denida en tu URLconf. prueba ejecutar el servidor de desarrollo Django e intenta acceder a una página Web como revisión 789 del 9 de noviembre de 2009 . La utilidad de esta página va más allá del mensaje básico de error 404. En versiones anteriores del framework Web Python CherryPy (http://www.

5.5.2: Página 404 de Django revisión 789 del 9 de noviembre de 2009 . ERRORES 404 19 Figura 5.

Comodines en los patrones URL Continuando con nuestro ejemplo coincida con uno o más dígitos: hours_ahead. en la cual la hora será designada por el de la cadena de consulta de la URL (la parte a continuación de ?). si es que realmente quieres saberlo).6. pongámosle un comodín al patrón URL. algo como parámetro hours /time/plus?hours=3. haciendo más fácil el usarlas que el no usarlas. Como ya se mencionó antes. ½Oye. un patrón URL es una expresión regular. ) Este patrón coincidirá con cualquier URL que sea como Ahora que lo pienso. sino también la aplicación estará limitada a admitir sólo el rango horario denido -.views import current_datetime.1. más simple. de aquí.2}: (r'^time/plus/\d{1. hours_ahead).conf. /time/plus/3 es mucho más limpia.2. (r'^time/plus/1/$'.la fecha/hora actual -. pero la URL Vamos a crear una segunda vista que nos muestre la fecha y hora actual con un adelanto de ciertas horas. aunque largo. o también /time/plus/100000000000/ \d{1. En la mayoría de las aplicaciones Web. Eso signica que queremos tener números de uno o dos dígitos en la sintaxis de las expresiones regulares. lo que resultaría una URLconf como esta: urlpatterns = patterns(''. y así. La URL de consulta. Y. (r'^time/plus/\d+/$'. con lo que nos quedaría así /time/plus/2/. 5. LOS PRINCIPIOS DE LAS PÁGINAS WEB DINÁMICAS 5. (r'^time/$'. /time/plus/25/. la URL contiene parámetros que inuyen en la salida de la página. . más fácil de dictarse a alguien y .defaults import * from mysite. Aquí hemos limitado a los exagerados reconociendo lapsos de hasta 99 horas. Algunas palabras acerca de las URLs bonitas Si tienes experiencia en otra plataforma de diseño Web. current_datetime).2}/$'. Las URLs bonitas son un signo de calidad en las aplicaciones Web. Nota Cuando construimos aplicaciones Web. one_hour_ahead). como PHP o Java. ) Claramente. Si. No sólo porque producirá redundancia entre las funciones de vista. usemos un parámetro cadena de consulta!. es posible que estés pensado. sería un nombre fantástico para una banda musical. Aquí necesitamos algo de abstracción.urls. . Con Django puedes hacer eso (pero te diremos cómo más adelante. 5. esta línea de pensamiento es incorrecta.6. A un novato se le ocurriría escribir una función de vista distinta para cada adelanto de horas. four_hours_ahead). de repente. two_hours_ahead). (r'^time/$'.6. y decidir si la aplicación admitirá o no esa entrada. revisión 789 del 9 de noviembre de 2009 . es que usamos el patrón de expresión regular \d+ para que from django. más legible. tendríamos que crear una vista distinta y una línea URLconf. hours_ahead urlpatterns = patterns(''. (r'^time/plus/2/$'.20 CAPÍTULO 5. Los Limitadores exagerados. El objetivo es montar un sitio en la que la página la página /time/plus/2/ /time/plus/1/ muestre la fecha/hora una hora más adelantada. En la primer vista de ejemplo. siempre es importante considerar el caso más descabellado posible de entrada. sin embargo. Tu segunda Vista: URLs dinámicas (/time) era estática. tres o cuatro horas. (r'^time/plus/3/$'. podemos limitar el lapso máximo de horas en 99. quisiéramos crear una página que mostrara la hora cinco horas adelantada.uno.eran dinámicas. pero una de las losofías del núcleo de Django es que las URLs deben ser bonitas. por cierto. muestre la fecha/hora dos horas más adelantada. el contenido de la página -. perpetuando la duplicación y la demencia. current_datetime). dos. (r'^time/plus/4/$'. three_hours_ahead). hours_ahead). la página /time/plus/3/ muestre la fecha/hora tres horas más adelantada. justamente más bonita que su homóloga forma de cadena El sistema de URLconf que usa Django estimula a generar URLs bonitas.

sólo que con una diferencia: tomará views. Si eres del tipo de programadores que les gusta ir de abajo hacia arriba. /time/plus/3/. it will be %s. Si eres del tipo de programadores que piensan globalmente. vista que escribimos antes. dt) return HttpResponse(html) Repasemos el código anterior línea a línea: Tal como hicimos en la vista el módulo datetime. hours_ahead).py lo siguiente: import django. Notar que la cadena de caracteres capturada Si la petición URL fuera es la cadena de caracteres capturada por los paréntesis en el patrón URL. y luego asociarlas a URLs. incluyendo la vista anterior current_datetime.http.2})/$'.2}: (r'^time/plus/(\d{1. al igual que en current_datetime. En el caso del ejemplo. escribimos la vista primero y luego el patrón de URL.HttpResponse y toma dos parámetros: La función de vista hours_ahead. y después el código de las funciones de vista.now() + datetime. tal vez preeras escribir las funciones de vista primero. TU SEGUNDA VISTA: URLS DINÁMICAS 21 Ahora designaremos el comodín para la URL. el número de horas que debemos adelantar. vamos a escribir la vista hours_ahead. queremos guardar cualquier número que se anotará en la URL. te sentirás como en casa aquí. request y offset. hours_ahead urlpatterns = patterns(''. Lo haremos colocando paréntesis alrededor de los datos en el patrón URL que querramos guardar. entonces el offset caracteres '21'. un argumento extra. revisión 789 del 9 de noviembre de 2009 .timedelta(hours=offset) html = "<html><body>In %s hour(s). Orden para programar En este ejemplo.datetime.http. La URLconf nal. necesitamos una forma de pasar esa información a la función de vista. • request • offset es un objeto vamente: cada vista siempre toma un objeto HttpRequest. importamos la clase django. hours_ahead).2})/$'. Si estás familiarizado con las expresiones regulares.5. todo se reduce a elegir qué técnica se amolda más a tu cerebro. Al nal. current_datetime). current_datetime.HttpResponse import datetime def hours_ahead(request. offset): offset = int(offset) dt = datetime. nos quedará algo así: from django.6.defaults import * from mysite. Esto también está bien. al inicio del proyecto. ) Con cuidado. y es esencial denir los parámetros requeridos por las funciones de vista que necesitaremos desarrollar. pero en el ejemplo anterior. Por ejemplo. puede que tenga más sentido que escribas todos los patrones de URL para la aplicación al mismo tiempo. entonces el offset debería ser la /time/plus/21/.</body></html>" % (offset. Agrega al archivo hours_ahead es muy similar a current_datetime.urls. no un entero. si la petición URL fuera cadena de caracteres '3'. ¾Qué técnica es mejor? Bien. Ambos enfoques son válidos. entonces pongamos paréntesis alrededor de \d{1. (r'^time/plus/(\d{1. debería ser la cadena de como en el caso siempre es una cadena de caracteres. (r'^time/$'. cada programador es diferente. incluso si se compone sólo de dígitos. primero escribimos el patrón URL y en segundo lugar la vista.conf. estamos usando paréntesis para capturar los datos del texto que coincide.views import current_datetime. Lo diremos nueHttpRequest como primer parámetro. '21'. Esto tiene la ventaja de darnos una lista de objetivos clara. así podremos usar una sola función de vista para cualquier adelanto de hora.

py del ejemplo anterior sólo por claridad). Poniéndolas juntas. calculamos la hora actual más las hora que tiene la variable dt. dt) return HttpResponse(html) revisión 789 del 9 de noviembre de 2009 . pero puedes asignarle el nombre que quieras.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime. como lo sería la cadena de caracteres la variable ValueError si se llama a la función int() con un 'foo'.0. Luego prueba http://127.datetime. Python con dos valores. La función datetime. es que usamos el formato de cadenas de (offset. en lugar de posición. .0. offset): offset = int(offset) dt = datetime. Sin embargo.timedelta offset. http://127. almacenando el resultado en requiere que el parámetro hours sea un entero.timedelta(hours=offset) html = "<html><body>In %s hour(s).7. Esto ilustra en este ejemplo no debemos preocuparnos de atrapar la excepción. Si estás siguiendo el libro y programando al mismo tiempo. HttpResponse del HTML -. y ahora = int(offset) ½rompámosla! Introduzcamos deliberadamente un error de Python en el archivo de la vista hours_ahead: def hours_ahead(request. Lo primero que hacemos en la función es llamar a cadena de caracteres a entero.0. int() con offset. construimos la salida HTML de esta función de vista. hay dos símbolos %s en la cadena de caracteres y la tupla de valores a insertar sería: Finalmente.2})-captura sólo dígitos. LOS PRINCIPIOS DE LAS PÁGINAS WEB DINÁMICAS Decidimos llamar a la variable offset. todo lo que importa es lo que contiene el segundo parámetro de la función (luego de URLconf. y visita car que el patrón en la URLconf sólo acepta número de uno o dos dígitos.1:8000/time/plus/5/. tal como vimos en la sección  Errores 404 anteriormente. como argumentos en la int() sobre offset. Eso lo veremos en detalle en el Capítulo 8. Django debería mostrar un error en este caso como Page not found. (Omitimos la vista algo similar a esto: Con esta función de vista y la URLconf escrita. Esto convierte el valor de la valor que no puede convertirse a un entero. El nombre de la variable no importa. notarás que el archivo vistas.1:8000/time/plus/100/ para veririendo).</body></html>" % (offset. La URL (sin horas designadas) debería también mostrar un error 404.1:8000/time/plus/ ahora contiene dos current_datetime views. ejecuta el servidor de desarrollo de Django (si no está cor- http://127. siemrequest).py comentando la línea offset Tomémonos un momento para admirar la bonita aplicación web que hemos creado hasta ahora . A continuación. veríamos from django. (\d{1.1:8000/time/plus/3/ para vericar que lo que hicimos funciona.</body></html>" % (offset.timedelta(hours=offset) html = "<html><body>In %s hour(s). it will be %s. offset): #offset = int(offset) dt = datetime.0. Tener en cuenta que Python lanzará una excepción Es posible también usar una palabra clave.now() + datetime. Sabemos esto. Por lo tanto. La siguiente línea de la función muestra la razón por la que se llamó a la función En esta línea.now() html = "<html><body>It is now %s.datetime. Para terminar visita http://127.22 CAPÍTULO 5.0. por el patrón URL de la expresión regular en el URLconf -- otra ventaja de tener un URLconf: nos provee un primer nivel de validación de entrada.0. retornamos el Una pequeña diferencia en esta línea. .now() + datetime. Páginas de error bonitas con Django views.de nuevo. dt) return HttpResponse(html) 5. no sólo uno. pre que sea un identicador válido para Python.0. it will be %s. porque tenemos la certeza que offset será una cadena de caracteres conformada sólo por dígitos.</body></html>" % now return HttpResponse(html) def hours_ahead(request. tal como lo hicimos en la vista current_datetime.0. dt). tal como hicimos en la vista current_datetime.

expone las entrañas del código fuente de Python.timedelta espera que el parámetro hours sea un entero. la sección Request information incluye una gran cantidad de información sobre la petición Web que provocó el error: información GET y POST. y el número de línea que contiene el error. Nota el texto Switch to copy-and-paste view debajo de la cabecera Traceback . A continuación. esto es casi siempre es una mala idea. Eso causa que datetime. Aquí comentamos algunas cosas a destacar: En la parte superior de la página se muestra la información clave de la excepción: el tipo y cualquier parámetro de la excepción (el mensaje "unsupported type" en este caso). para provocar una página de error. Lo abordaremos más tarde. Explicaremos cómo desactivar este modo más adelante. Abajo de la información clave de la excepción. quita el comentario en la línea normalmente de nuevo. sólo mira los ajustes para tener una idea de la información disponible.8. Más abajo. Dediquemos un momento a explorar esta página y descubrir las distintas piezas de información que nos brinda. (Hay maneras más avanzadas de depurar las vista en Django. Django muestra el nombre del archivo. Ejecuta el servidor de desarrollo y navega a signicativa. cubre en detalle los ajustes de conguración disponibles. Usando esto para cuando necesitemos compartir el traceback de la excepción con otros para obtener soporte técnico -.sin la sentencia temporalmente podemos insertar un offset = int(offset) para que la función de vista funcione ¾Eres el tipo de programador al que le gusta depurar con la ayuda de sentencias assert False print cuidadosamente colocadas? print. Este es el *traceback* de Python para *traceback* estándar que se obtiene en el interprete de Python. Puedes usar la página de error de Django para hacer eso -. y el *traceback* cambiará a una versión que te permitirá fácilmente copiar y pegar. como por ejemplo. Es el típico pequeño bug que todo programador comete en algún momento. la página muestra el dicha excepción. En cualquier punto de la vista. Por cada marco de la pila. sólo que más interactivo. la función datetime. cuando discutamos el sistema de plantillas de Django.5. es obvio que la mayor parte de la información es delicada -. Inline literal start-string without end-string. el número de línea. Haz click en esas palabras. y hemos comentado la línea de código que realiza la conversión del offset a entero. Luego. Por esta razón. Inline literal start-string without end-string. Verás una página de error con mucha información TypeError mostrado en la parte superior de la página: "unsupported type for timedelta hours component: str". Finalmente. y el código fuente de esa línea. El  Apéndice E`_`_. ¾Qué ha ocurrido? Bueno. podremos ver las variables locales y el estado del programa. Haz click en Locals vars debajo de cualquier marco de la pila para ver la tabla de todas las variables locales y sus valores. con Django podemos hacer esto con un potente motor de revisión 789 del 9 de noviembre de 2009 . lo que nos brinda un poco de contexto.como los amables colegas que encontraremos en el canal de IRC o la lista de correo de Django. el archivo en el cuál la excepción fue lanzada. la sección Settings lista la conguración de la instalación de Django en particular. Por ahora. Haz click en la línea de código (en gris oscuro) para ver varias líneas anteriores y posteriores a la línea errónea. Por ahora. en ese marco y en la posición exacta de código en el cual fue lanzada la excepción. El  Apéndice H`_`_ es una completa referencia sobre toda la información que contienen los objetos peticiones. el nombre de la función/método. la página de error es mostrada sólo cuando el proyecto está en modo depuración. trabajan de manera similar. descriptos en la sección  Errores 404. valores de las cookies y meta información como las cabeceras CGI. Esta información de depuración es invaluable. La página de error de Django es capaz de mostrar más información en ciertos casos especiales.y sería una estupidez mostrarla al público en Internet. incluyendo el mensaje El punto de este ejemplo fue demostrar la página de error de Django.8. ¾Qué sigue? Hasta ahora hemos producido las vistas mediante código HTML dentro del código Python. ¾QUÉ SIGUE? 23 /time/plus/3/. lo explicaremos más adelante. Una persona con malas intenciones podría usar esto para intentar aplicar ingeniería inversa en la aplicación Web y hacer cosas maliciosas. (¾Suena familiar? Los errores Page not found. como también de la conguración de Django -. en el caso de error de sintaxis en las plantillas. hay que tener en claro que todos los proyectos de Django están en modo depuración automáticamente cuando son creados. Por ahora.timedelta lance un TypeError.) 5. Desafortunadamente. Pero por suerte. pero esto es la forma más rápida y fácil).

Nos sumergiremos en el motor de plantillas de Django en el `próximo capítulo`_ Duplicate explicit target name: próximo capítulo.24 CAPÍTULO 5. LOS PRINCIPIOS DE LAS PÁGINAS WEB DINÁMICAS plantillas que nos permite separar el diseño de las páginas del código fuente subyacente. malformed hyperlink target. malformed hyperlink target. unexpected unindent. Explicit markup ends without a blank line. revisión 789 del 9 de noviembre de 2009 .

Asimismo. más bien que una persona espere por otra a que termine de editar un solo archivo que contiene ambos: Python y HTML.</p> <p>Thanks for placing an order from {{ company }}. esto es más eciente si los programadores pueden trabajar sobre el código Python y los diseñadores sobre las plantillas al mismo tiempo. Escribir código Python y diseñar HTML son dos disciplinas diferentes. el cual trataremos en este capítulo. y la mayoría de los entornos de desarrollo web profesional dividen estas responsabilidades entre personas separadas (o incluso en departamento separados). es mucho más limpio y mantenible separar el diseño de la página del código Python en sí mismo. quizás notaste algo extraño en cómo retornábamos el texto en nuestras vistas de ejemplos. Vamos a sumergirnos en una simple plantilla de ejemplo. Podemos hacer esto con el sistema de plantillas de Django. El diseño de un sitio tiende a cambiar más frecuentemente que el código de Python subyacente. It's scheduled to ship on {{ ship_date|date:"F j. Normalmente. las plantillas son usadas para producir HTML. Por esas razones.</p> <p>Here are the items you've ordered:</p> <ul> { % for item in item_list %} <li>{{ item }}</li> { % endfor %} </ul> revisión 789 del 9 de noviembre de 2009 . Este convenio conduce a problemas severos: Cualquier cambio en el diseño de la página requiere un cambio en el código de Python. Sistema básico de plantillas Una plantilla de Django es una cadena de texto que pretende separar la presentación de un documento de sus datos. A saber. Diseñadores y programadores HTML/CSS no deberían tener que editar código Python para conseguir hacer su trabajo. Piensa en esto como un modelo de carta: <html> <head><title>Ordering notice</title></head> <body> <p>Dear {{ person_name }}. por lo que sería conveniente si el diseño podría ser cambiado sin la necesidad de modicar el código Python. Esta plantilla describe una página HTML que agradece a una persona por hacer un pedido de la empresa. 6. ellos deberían tratar con HTML. Y" }}. Una plantilla dene rellenos y diversos bits de lógica básica (esto es. etiquetas de plantillas) que regulan cómo debe ser mostrado el documento.Capítulo 6 El sistema de plantillas de Django En el capítulo anterior. el HTML fue codicado 2 directamente en nuestro código Python. pero las plantillas de Django son igualmente capaces de generar cualquier formato basado en texto.1.

Llama al método Este retorna una plantilla totalmente renderizada como una cadena de caracteres. Vamos a llegar a eso en un momento.2. algunos de los cuales serán tratados en la sección que sigue. dejándote recorrer a través de cada uno if. la variable {{ ship_date|date:"F j. el código en crudo de la plantilla. el sistema de plantillas mostrará todo lo que hay entre { % if ordered_warranty %} y { % endif %}. Cualquier texto que esté rodeado por llaves y signos de porcentaje (por ej. Vamos a sumergirnos en el intérprete interactivo de Python para ver cómo funciona este código. el sistema de plantillas no mostrará esto. Cada plantilla de Django tiene acceso a varias etiquetas y ltros incorporados. En este ejemplo. EL SISTEMA DE PLANTILLAS DE DJANGO { % if ordered_warranty %} <p>Your warranty information will be included in the packaging. como una referencia a las tuberías de Unix. módulo Creación de objetos Template La manera fácil de crear objetos Template es instanciarlos directamente. como quizás esperabas. el segundo párrafo de esta plantilla tiene un ejemplo de un ltro.2. Si lo hace. con todas las variables y etiquetas de bloques evaluadas de acuerdo al contexto. Esto signica insertar el valor de la variable a la que se dio ese nombre. 6. actúa como una cláusula lógica if . tomando el ltro date el argumento "F j. Vamos paso a paso a través de ésta: Cualquier texto encerrado por un par de llaves (por ej. Una etiqueta de los items de una secuencia. Una etiqueta Finalmente. En este caso en particular. de modo que sepas qué es posible. También es posible crear tus propios ltros y etiquetas. Este ejemplo de plantilla contiene dos etiquetas: la etiqueta etiqueta {{ person_name }}) es una variable. Los ltros se encadenan mediante el uso de un caracter pipe (|). especicado por ese argumento. el contexto). La denición de etiqueta es bastante amplia: una etiqueta sólo le indica for) y la etiqueta { % if ordered_warranty %} (una etiqueta { % for item in item_list %} if).1. (una for actúa como un simple constructor de bucle. Crea un objeto también ofrece un camino para crear objetos Template brindando el código en crudo de la plantilla como una cadena. El sistema de plantillas también admite { % else %} y otras varias cláusulas lógicas. los cuales cubriremos en el `Capítulo 10`_. la etiqueta comprueba si el valor de la variable ordered_warranty se evalúa como True. La clase Template se encuentra en el django. Empleo del sistema de plantillas Para usar el sistema de plantillas en el código Python. Y". El Apéndice F contiene la lista completa de etiquetas y ltros. estamos pasando ship_date por el ltro date. Las siguientes secciones describen cada uno de los pasos con mayor detalle. ¾Cómo especicamos el valor de { % if ordered_warranty %}) es una etiqueta de plantilla.<br />{{ company }}</p> </body> </html> Esta plantilla es un HTML básico con algunas variables y etiquetas de plantillas agregadas. vamos a examinar esto en un rato. El ltro date formatea fechas en el formato dado. Si no.template. sólo sigue estos dos pasos: 1. y el constructor toma un argumento.26 CAPÍTULO 6. y es una buena idea familiarizarse con esta lista. las variables?. con el cual puedes alterar la exposición de una variable. revisión 789 del 9 de noviembre de 2009 . Django Template especicando la ruta al archivo de plantilla render() del objeto Template con un conjunto de variables (o sea. 6. en el sistemas de archivos. Y" }}. 2. al sistema de plantilla haz algo.</p> { % endif %} <p>Sincerely.

la llamada a TemplateSyntaxError: Template() >>> from django.. print value >>> my_function('hello') hello Esos tres puntos al comienzo de una línea adicional son insertados por el shell de Python -.no son parte de nuestra entrada. EMPLEO DEL SISTEMA DE PLANTILLAS 27 Ejemplos del Intérprete Interactivo Durante todo el libro.template. por >>> print """This is a . escribe python manage.py shell django-admin. string that spans .py startproject (como se expuso en el Capí- para comenzar el intérprete interactivo. Desde dentro del directorio del proyecto creado por tulo 2). ejemplo: Puedes reconocer estos ejemplos por el triple signo mayor-que (>>>).. suele usarse en el Apéndice E. verás algo como esto: <django. no copies esos puntos.. Cuando creas un objeto causará una excepción python manage. es la forma simple en que Python identica un Variables de conguración de Django Cuando usas Django. el sistema de plantillas compila el código en crudo a uno interno.template.. Si estás copiando los ejemplos del libro. Interactivamente.TemplateSyntaxError: Invalid block tag: 'notatag' El sistema lanza una excepción TemplateSyntaxError por alguno de los siguientes casos: Bloques de etiquetas inválidos Argumentos inválidos para una etiqueta válida Filtros inválidos Argumentos inválidos para ltros válidos Sintaxis inválida de plantilla Etiquetas de bloque sin cerrar (para etiquetas de bloque que requieran la etiqueta de cierre) revisión 789 del 9 de noviembre de 2009 . y realmente no importa.6... Pero si tu código de plantilla incluye errores de sintaxis. distinto cada vez. hemos expuesto sesiones de ejemplo del intérprete Python interactivo. del intérprete. line 1. de forma optimizada. pero tienes otras opciones descriptas Template.2. Si estás copiando nuestros ejemplos para seguirlos. no copies estos signos mayor-que. three lines...py shell. necesitas indicarle qué valores usar para sus variables de conguración. Los incluimos aquí para ser eles a la salida real del intérprete. Aquí hay un ensayo básico: >>> from django. el cuál designa el prompt Las sentencias multilíneas en el intérprete interactivo son rellenadas con tres puntos (..).""" This is a string that spans three lines..") >>> print t Si lo estás siguiendo interactivamente. listo para renderizar.Template object at 0xb7d5f24c> Ese objeto de 0xb7d5f24c será Template. django. >>> def my_function(value): .template import Template >>> t = Template('{ % notatag %} ') Traceback (most recent call last): File "<stdin>".template import Template >>> t = Template("My name is {{ name }}. in ? .

.. to ship on {{ ship_date|date:"F j. 'ordered_warranty': True}) >>> t.template import Context. Un contexto es simplemente Context.. Importamos el módulo pasándole al constructor de la clase datetime desde la biblioteca estándar de Python..</p> .. 4. en el código Python.. creamos un objeto plantilla. { % endif %} . Este es un ejemplo de compilación y renderización de una plantilla. { % if ordered_warranty %} . le puedes pasar datos brindando un contexto...<br />Outdoor Equipment</p>" Vamos paso a paso por este código. Renderizar una plantilla Una vez que tienes un objeto de bloque. (Los puntos son un caso especial al que llegaremos en un momento). . comillas para delimitar la cadena de caracteres.. . 2009. 'product': 'Super Lawn Mower'.. It's scheduled . .2.. importamos la clase Guardamos en texto crudo Template y Context.</p> . Note que usamos triple t.28 CAPÍTULO 6.. Su constructor toma un argumento opcional: un diccionario que mapea nombres de variables con valores. Context >>> raw_template = """<p>Dear {{ person_name }}. guiones bajos y puntos. ..</p>\n\n\n <p>Your warranty information will be included in the packaging.. ambas se encuentran en el módulo django. . 'ship_date': datetime. raw_template Template.") c = Context({"name": "Stephane"}) t. Un es similar a un diccionario. Template. EL SISTEMA DE PLANTILLAS DE DJANGO 6.</p> . Context Context provee funcionalidad adicional.render(c) name is Stephane... <p>Sincerely.2.</p>\n\n<p>Thanks for ordering Super Lawn Mower from Outdoor Equipment. de nuestra plantilla en la variable raw_template.. . Una plantilla usa estas variables para llenar y evaluar estas etiquetas Un contexto es representado en Django por la clase método django.template.template import Template.. de a una sentencia a la vez: Primero. porque lo vamos a necesitar revisión 789 del 9 de noviembre de 2009 en la próxima sentencia.' Diccionarios y Contextos Un diccionario de Python es un mapeo entre llaves conocidas y valores de variables. .date(2009.. las cadenas de caracteres delimitadas con una sola comilla indican que no puede abarcar varias líneas. Los nombres de variables son sensible a mayúsculas-minúsculas.. 'company': 'Outdoor Equipment'.. It's scheduled \nto ship on April 2.render(c) "<p>Dear John Smith. 2). debido a que abarca varias líneas.</p>\n\n\n <p>Sincerely. Template t = Template("My name is {{ name }}. <p>Thanks for ordering {{ product }} from {{ company }}.template.. Llama al render() del objeto Template con el contexto para llenar la plantilla: >>> >>> >>> >>> 'My from django. Luego. ésta se encuentra en el módulo un conjunto de variables y sus valores asociados. <p>Your warranty information will be included in the packaging. Y" }}.. como se cubre en el Los nombres de las variables deben comenzar con una letra (A-Z o a-z) y pueden contener dígitos..<br />{{ company }}</p>""" >>> t = Template(raw_template) >>> import datetime >>> c = Context({'person_name': 'John Smith'. pero un `Capítulo 10`_. . usando la plantilla de muestra del comienzo de este capítulo: >>> from django...

crea un objeto crea un Context.6. es más eciente crear el objeto Template una sola vez y luego llamar a render() sobre éste muchas veces: # Bad for name in ('John'. en vez de imprimir el valor de la cadena. Esto sucede porque es una sutileza del intérprete interactivo de Python: la llamada a t. {{ name }}') >>> print t. product es 'Super Lawn Mower'. Context >>> t = Template('Hello. 'Pat'): t = Template('Hello. Template. pasamos valores simples a los contextos --en su mayoría cadena de caracteres. Para acceder al valor de ese diccionario por su clave. por ejemplo. c. llamamos al método texto. es mostrada acorde al formato de cadena de carac- (Explicaremos los formatos de cadenas de caracteres para el ltro Si eres nuevo en Python.render(Context({'name': 'John'})) Hello. Julie >>> print t. Sin embargo.2. EMPLEO DEL SISTEMA DE PLANTILLAS 29 Entonces. Aquí. reemplaza las variables de la plantilla con los valores reales de las variables. más un datetime. Por ejemplo. quizás te preguntes por qué la salida incluye los caracteres de nueva línea ('\n') en vez de mostrar los saltos de línea. Este retorna la plantilla renderizada -. plantilla. También nota que la fecha April 2.4. la mayoría de los analizadores pasan con una simple llamada a una expresión regular corta. teres Nota que el párrafo de garantía fue mostrado porque la variable F j. Si quieres ver la cadena de caracteres con los saltos de líneas como verdaderos saltos de líneas en vez de caracteres '\n'. Estos son los fundamentos del uso del sistema de plantillas de Django: sólo escribe una plantilla. Y.render(c). Detrás de escena. {{ name }}') for name in ('John'. como listas. y llama al método Múltiples contextos.date. especicamos que Context . Esto es un claro contraste con el motor de plantillas de XML.render(Context({'name': 'Pat'})) Hello. y el intérprete interactivo.). render() sobre nuestro objeto de plantilla. mismas plantillas Una vez que tengas un objeto Template. el sistema de plantillas maneja elegantemente estructuras de datos más complicadas. y ejecuta cualquier bloque de etiquetas. por omisión. índices o métodos de un objeto. person_name 'John Smith'. True.2. por ejemplo: >>> from django. usa la sentencia print: print t.esto es. Pat Cuando estés usando la misma plantilla fuente para renderizar múltiples contextos como este.render(Context({'name': name})) El análisis sintáctico de las plantillas de Django es bastante rápido. atributos. 'Julie'.template import Template. y tiende a ser órdenes de magnitud más lento que el motor de renderizado de Django.2. puedes renderizarlo con múltiples contextos. 6. diccionarios y objetos personalizados.render(c) retorna una cadena de caracteres. Búsqueda del contexto de una variable En los ejemplos dados hasta el momento. 'Pat'): print t. imagina que pasas un diccionario de Python a una punto para acceder a las claves de un diccionario.3. ordered_warranty date se evalúa como a la brevedad). creamos un objeto es el cual mapea nombres de variables con valores. pasando a éste el con- Finalmente. {{ name }}') print t. y así sucesivamente. 2009. que incurre en la excesiva actividad de un analizador XML. usa el punto: revisión 789 del 9 de noviembre de 2009 . muestra una representación de ésta. 'Julie'. Usa un Esto es mejor ilustrarlos con algunos ejemplos. 6. John >>> print t.render(Context({'name': 'Julie'})) Hello. La clave para recorrer estructuras de datos complejas en las plantillas de Django ese el carácter punto (.render(Context({'name': name})) # Good t = Template('Hello. render(). El constructor de Context toma un diccionario de Python.

{{ var. def __init__(self. last_name >>> t = Template('Hello.2 }}. Finalmente.template import Template...date(1993.' TemplateSyntaxError. Los índices negativos de las listas no están permitidos. first_name. {{ person.True' Nota que no tienes que incluir los paréntesis en las llamadas a los métodos.False' >>> t. Además.' Los puntos también son utilizados para llamar a métodos sobre los objetos.last_name }}.age }} years old.first_name.month 5 >>> d. los puntos también son usados para acceder a los índices de las listas. Por ejemplo.render(c) 'Item 2 is carrots.year }}. y puedes usar el punto para acceder a ellos en las plantillas de >>> from django. 2) >>> d.. EL SISTEMA DE PLANTILLAS DE DJANGO >>> from django. Por ejemplo. (Explicaremos esta losofía luego en este capítulo).first_name }} {{ person.render(c) 'The month is 5 and the year is 1993. tampoco es posible pasar argumentos a los métodos. por ejemplo: >>> from django. self. Context >>> person = {'name': 'Sally'.year 1993 >>> d.date Django: De forma similar.' Este ejemplo usa una clase personalizada: >>> from django. 'carrots']}) >>> t.render(c) 'Hello.last_name = first_name. 'age': '43'} >>> t = Template('{{ person. month y day.') >>> c = Context({'person': person}) >>> t. un objeto de Python tiene los atributos year. Context >>> t = Template('Item 2 is {{ items. cada cadena de caracteres de Python tiene el métodos sintaxis de punto: upper() y isdigit(). 'bananas'.isdigit }}') >>> t. sólo puedes llamar los métodos que no requieran argumentos.day 2 >>> t = Template('The month is {{ date..') >>> c = Context({'items': ['apples'.30 CAPÍTULO 6.render(Context({'var': '123'})) '123 -. la variable {{ items. los puntos te permiten acceder a los atributos de los objetos.template import Template. Por ejemplo.name }} is {{ person. John Smith.') >>> c = Context({'person': Person('John'. Context >>> t = Template('{{ var }} -. self.month }} and the year is {{ date.') >>> c = Context({'date': d}) >>> t. 'Smith')}) >>> t.template import Template.{{ var.HELLO -.render(Context({'var': 'hello'})) 'hello -.template import Template. y puedes llamar a estos en las plantillas de Django usando la misma >>> from django.upper }} -.123 -. last_name): .template import Template. Context >>> class Person(object): .render(c) 'Sally is 43 years old. Context >>> import datetime >>> d = datetime. 5.' datetime.-1 }} causará una revisión 789 del 9 de noviembre de 2009 .

') >>> c = Context({'person': person}) >>> t. la variable será renderizada como un string >>> t = Template("My name is {{ person.. Si la silent_variable_failure. . permitir que el sistema de plantillas tenga acceso a ellos. a menos que la excepción tenga un atributo excepción tiene el atributo vacío. el que se traduce en una búsqueda de diccionario (person['name']) y luego en una llamada a un método (upper()): El sistema utiliza el primer tipo de búsqueda que funcione. tienes objeto debería permitir incluir algo como Para prevenir esto. La búsqueda del punto puede resumirse como esto: cuando un sistema de plantillas encuentra un punto en una variable. entonces el primer elemento es el 0.2. "foo" >>> p = PersonClass3() >>> t. 'age': '43'} >>> t = Template('{{ person.template import Template.6.. por ejemplo.render(Context({"person": p})) Traceback (most recent call last): . asigna el BankAccount que tiene un método delete(). AssertionError: foo >>> .. el segundo es el 1 y así sucesivamente.age }} years old.bar) Llamada de método (por ej. la excepción será propagada.. Aquí hay algunas cosas a tener en cuenta: Si. éste intenta la siguiente búsqueda.name. Context >>> person = {'name': 'Sally'. en el mejor de los casos. por ejemplo: silent_variable_failure cuyo valor sea True. foo. en este orden: foo["bar"]) Atributo (por ej.first_name }}. Digamos. En otro caso.. foo[bar]) Diccionario (por ej. Evidentemente. atributo alters_data de la función en el método: Una plantilla no revisión 789 del 9 de noviembre de 2009 . def first_name(self): . foo. el sistema pasará a la siguiente búsqueda de tipo (índice de lista). Es la lógica de cortocircuito.. >>> >>> "My class SilentAssertionError(AssertionError): silent_variable_failure = True class PersonClass4: def first_name(self): raise SilentAssertionError p = PersonClass4() t..' Comportamiento de la llamada a los métodos La llamada a los métodos es ligeramente más compleja que los otros tipos de búsqueda. un método provoca una excepción.. {{ account. >>> from django.bar()) Índice de lista (por ej.. por lo que sería absurdo. El siguiente ejemplo usa {{ person.upper }}." La llamada a un método funcionará sólo si el método no requiere argumentos.delete }}.name. raise AssertionError. algunos métodos tienen efectos secundarios. EMPLEO DEL SISTEMA DE PLANTILLAS 31 Listas de Python Las listas de Python comienzan en cero.") >>> class PersonClass3: . >>> ..upper }} is {{ person.. Los puntos pueden ser anidados a múltiples niveles de profundidad.render(c) 'SALLY is 43 years old..render(Context({"person": p})) name is . y posiblemente un agujero de seguridad. durante la búsqueda de método.

6. Discutiremos esto más adelante en el `Capítulo 10`_. Context >>> t = Template('Your name is {{ name }}.render(Context({'NAME': 'hello'})) 'Your name is . Este ¾Cómo se manejan las variables inválidas? Por omisión. por ejemplo: >>> from django. también.' >>> t. Jugando con objetos Context La mayoría de la veces. por ejemplo: { % if today_is_weekend %} <p>Welcome to the weekend!</p> { % endif %} revisión 789 del 9 de noviembre de 2009 .alters_data = True El sistema de plantillas no debería ejecutar cualquier método marcado de este modo. Ten en cuenta que es posible cambiar el comportamiento por omisión de Django en este sentido. fallando silenciosamente.delete }}.3.render(Context({'Name': 'hello'})) 'Your name is .3. el sistema de plantillas se distribuye con etiquetas y ltros incorporados.' >>> t. no está vacía y no es un valor Boolean { % if %} y { % endif %}. En otras palabras. si una variable no existe. el sistema de plantillas renderiza este como un string vacío.2. usando la >>> from django.1. si una plantilla incluye fallará silenciosamente. instancias un objeto sintaxis estándar de los diccionarios de Python: Pero puedes agregar y quitar elementos de un objeto Context pasando un diccionario completamente poblado a Context. En el mundo real.render(Context()) 'Your name is . el sistema mostrará todo lo que hay entre { % if %} evalúa una variable.32 CAPÍTULO 6.render(Context({'var': 'hello'})) 'Your name is . esta etiqueta no ejecutará el método delete().template import Template. todas las búsquedas fallan porque los nombres de las variables.' >>> t. {{ account. En este caso. es inaceptable para un sitio web ser inaccesible debido a un error de sintaxis tan pequeño. o su capitalización es incorrecta. EL SISTEMA DE PLANTILLAS DE DJANGO def delete(self): # Delete the account delete. ajustando la conguración de Django.5.' El sistema falla silenciosamente en vez de levantar una excepción porque intenta ser exible a los errores humanos.template import Context >>> c = Context({"foo": "bar"}) >>> c['foo'] 'bar' >>> del c['foo'] >>> c['foo'] '' >>> c['newvariable'] = 'hello' >>> c['newvariable'] 'hello' 6. Context una vez que éste está instanciado. Las secciones que siguen proveen un resumen de la mayoría de las etiquetas y ltros. if/else Etiquetas La etiqueta falso). y si esta es true (esto es. 6.') >>> t. existe. Etiquetas básicas de plantillas y ltros Como hemos mencionamos.

la tupla vacía (()).</p> { % else %} <p>No athletes are available. ETIQUETAS BÁSICAS DE PLANTILLAS Y FILTROS 33 La etiqueta { % else %} es opcional: { % if today_is_weekend %} <p>Welcome to the weekend!</p> { % else %} <p>Get back to work. considera efectuar la lógica en el código de la vista para simplicar las plantillas. Por ejemplo.6. Por ejemplo. and either coaches or cheerleaders! { % endif %} { % endif %} Usar varias veces el mismo operador lógico están bien. la cadena vacía (''). (OK. por ejemplo: { % if athlete_list %} { % if coach_list or cheerleader_list %} We have athletes. Si necesitas paréntesis. esto es válido: { % if athlete_list or coach_list or parent_list or teacher_list %} Ahí no hay una etiqueta { % elif %}. so writing English translations of Boolean logic sounds stupid. { % endif %} { % if not athlete_list or coach_list %} There are no athletes or there are some coaches. el diccionario vacío ({}).) { % endif %} { % if athlete_list and not coach_list %} There are some athletes and absolutely no coaches. la lista vacía ([]). esto es inválido: { % if athlete_list and coach_list or cheerleader_list %} No se admite el uso de paréntesis para controlar el orden de las operaciones. not La etiqueta { % if %} acepta and. pero no puedes combinar diferentes operadores. it's not our fault. Usa etiquetas { % if %} anidadas para conseguir alguna cosa: { % if athlete_list %} <p>Here are the athletes: {{ athlete_list }}.3. porque el orden de evaluación lógico puede ser ambiguo. el cero (0). { % endif %} { % if athlete_list or coach_list %} There are some athletes or some coaches. Por ejemplo: { % if athlete_list and coach_list %} Both athletes and coaches are available. o para testear múltiples variables. Todo lo demás es True. Aún así.</p> revisión 789 del 9 de noviembre de 2009 . o para negarlas.</p> { % endif %} Las verdades de Python En Python. si necesitas combinar hacer lógica avanzada. or. y el objeto especial None son False en un contexto booleano. { % endif %} Las etiquetas { % if %} no permiten las cláusulas and y or en la misma etiqueta. usa etiquetas { % if %} and y or para anidadas. { % endif %} { % if not athlete_list %} There are no athletes.

Cada vez que atravesamos el bucle.revcounter se la asignará el valor 1.) La etiqueta { % for %} asigna la variable forloop mágica a la plantilla con el bucle. a excepción de que está indexada a partir de cero. (Ve a la sección  Filosofía y limitaciones luego en este capítulo para comprender el razonamiento detrás de este decisión de diseño. forloop. En otro caso. y permite iterar sobre cada uno de los elementos de una secuencia. La última vez que se atraviese el bucle. La primera vez que se atraviesa el bucle.34 CAPÍTULO 6. el sistema de plantillas { % for %} { % endfor %}.counter bucle. forloop. es siempre asignada a un número entero representando el número de veces que se ha entrado en el bucle. Contendrá el valor 0 la primera vez que se atraviese el bucle.revcounter es siempre asignada a un entero que representa el número de iteraciones que faltan para terminar el bucle. forloop. Como en la sentencia dónde Y es la secuencia sobre la que se hace el bucle y X for es el nombre de la variable que se usará para cada uno de los ciclos del bucle.</p> { % endif %} { % endif %} Asegúrate de cerrar cada { % if %} con un { % endif %}. a elementos que hay en la secuencia menos 1.. Django levantará la excepción TemplateSyntaxError. Por ejemplo. La primera vez que se ejecuta el bucle forloop. el valor de esta 0.revcounter0 es como forloop. { % endfor %} Es posible anidar etiquetas { % for %}: { % for country in countries %} <h1>{{ country. Aquí un ejemplo: { % for item in todo_list %} <p>{{ forloop. excepto que esta es indexada a partir de cero. no hay apoyo para la sentencia continue que se encargue de retornar inmediatamente al inicio del bucle. Si quieres conseguir esto. cambia la variable sobre la que estás iterando para que incluya sólo los valores sobre los cuales quieres iterar..counter. EL SISTEMA DE PLANTILLAS DE DJANGO { % if coach_list %} <p>Here are the coaches: {{ coach_list }}.city_list %} <li>{{ city }}</li> { % endfor %} </ul> { % endfor %} No se admite la ruptura de un bucle antes de que termine.name }}</h1> <ul> { % for city in country. la sintaxis es renderizará todo entre for X in Y.counter }}: {{ item }}</p> { % endfor %} forloop.counter será 1. revisión 789 del 9 de noviembre de 2009 .revcounter0 es asignada al número de será igual al número de elementos que hay en la secuencia.counter0 es como forloop. De manera similar. for La etiqueta { % for %} de Python. puedes usar lo siguiente para mostrar una lista de atletas tomadas de la variable athlete_list: <ul> { % for athlete in athlete_list %} <li>{{ athlete. La última vez que se atraviese el bucle.revcounter será forloop. por lo que la primera vez que se ingresa al forloop.revcounter. Esta variable tiene algunos atributos que toman información acerca del progreso del bucle: forloop.name }}</li> { % endfor %} </ul> Agrega reversed a la etiqueta para iterar sobre la lista en orden inverso: { % for athlete in athlete_list reversed %} . Esta es indexada a partir de 1.

parentloop.counter }}</td> <td>City #{{ forloop. (Más sobre esta idea en la sección  Filosofía y limitaciones).parentloop esta es una referencia al objeto padre Aquí un ejemplo: de forloop.first es un valor booleano asignado a True si es la primera vez que se pasa por el bucle. Sin embargo. es bastante común que una plantilla requiera comparar dos valores y mostrar algo si ellos son iguales -Django provee la etiqueta La etiqueta endifequal %} { % ifequal %} para este propósito.city_list %} <tr> <td>Country #{{ forloop. Un uso común es para esto es poner un carácter pipe entre una lista de enlaces: { % for link in links %}{{ link }}{ % if not forloop.last %} | { % endif %}{ % endfor %} El código de la plantilla de arriba puede mostrar algo parecido a esto: Link1 | Link2 | Link3 | Link4 forloop. se llamará Generalmente no necesitas preocuparte por esto. able a de evitar sobreescribir la variable mágica la plantilla llamada forloop. si provees una vari- forloop. Este ejemplo compara las variables currentuser de la plantilla: { % ifequal user currentuser %} <h1>Welcome!</h1> { % endifequal %} Los argumentos pueden ser strings hard-codeados.last es un valor booleano asignado a True si es la última pasada por el bucle. en el caso de bucles anidados. El sistema de plantillas de Django a propósito no es un lenguaje de programación completo y por lo tanto no permite ejecutar sentencias arbitrarias de Python. con simples o dobles comillas. Esto es conveniente para ocasiones especiales: { % for object in objects %} { % if forloop.parentloop. Django expone ese contexto movido en recomendamos). { % for country in countries %} <table> { % for city in country. lo siguiente es válido: { % ifequal section 'sitenews' %} <h1>Site News</h1> { % endifequal %} { % ifequal section "community" %} <h1>Community</h1> { % endifequal %} revisión 789 del 9 de noviembre de 2009 . las variables existentes se mueven fuera de tal manera forloop. { % ifequal %} compara dos valores y muestra user y todo lo que se encuentra entre { % ifequal %} y {% si el valor es igual. forloop desaparece. Contextos y la variable forloop Dentro de un bloque { % for %}.6. Después de que el analizados sintáctico encuentra { % endfor %}. ETIQUETAS BÁSICAS DE PLANTILLAS Y FILTROS 35 forloop.3.parentloop ifequal/ifnotequal forloop (a pesar de que no lo mientras esté dentro del bloque { % for %}.first %}<li class="first">{ % else %}<li>{ % endif %} {{ object }} </li> { % endfor %} forloop.counter }}</td> <td>{{ city }}</td> </tr> { % endfor %} </table> { % endfor %} La variable mágica forloop está sólo disponible dentro de bucles.

la etiqueta comentario no será tomada como comentario): This is a {# this is not a comment #} test. el cual convierte el texto a minúscula. la salida del renderizado mostraría exactamente lo mismo que la plantilla (esto es. bio. comilla simple o comilla doble. Los argumentos de los ltros están siempre entre comillas addslashes: Agrega una con contra-barra antes de cualquier contra-barra. 6. el Apéndice F cubre el resto. Esto es útil si el texto producido está incluido en un string de JavaScript. no pueden ser comparados Estos ejemplos son inválidos: { % ifequal variable True %} { % ifequal variable [1. Aquí un modismo común para escapar contenido del texto. string.2. 2. 3] %} { % ifequal variable {'key': 'value'} %} Si necesitas comprobar cuando algo es verdadero o falso. Comentarios Al igual que en HTML o en un lenguaje de programación como Python. Para designar un comentario. la etiqueta { % ifequal %} admite un opcional { % else %}: { % ifequal section 'sitenews' %} <h1>Site News</h1> { % else %} <h1>No News Here</h1> { % endifequal %} ifequal %}. Esta limitación mejora la performance del analizador sintáctico de plantillas.23 %} 'foo' %} "foo" %} { % ifequal %}. Esto muestra el valor de {{ name }} después de aplicarle el ltro lower. el lenguaje de plantillas de Django permite comentarios. tales como diccionarios de Python. Un ltro con argumento se ve de este modo: {{ bio|truncatewords:"30" }} Esto muestra las primeras 30 palabras de la variable dobles.36 CAPÍTULO 6. En la siguiente plantilla. Filtros Como explicamos anteriormente en este capítulo. revisión 789 del 9 de noviembre de 2009 . Los siguientes son algunos de los ltros más importantes. Los ltros se parecen a esto: {{ name|lower }} Usa un pipe (|) para aplicar el ltro. Los ltros pueden estar en cadena -. y entonces convertir los saltos de líneas en etiquetas <p>: {{ my_text|escape|linebreaks }} Algunos ltros toman argumentos. la salida del uno de los ltros puede ser aplicada al próximo. usa la etiqueta { % if %} en vez de { % ifequal %}. enteros y números decimales son permitidos como argumentos para Estos son ejemplos válidos: {% ifequal ifequal ifequal ifequal variable variable variable variable 1 %} 1. usa {# #}: {# This is a comment #} Este comentario no será mostrado cuando la plantilla sea renderizada. Cualquier otro tipo de variables.eso es. los ltros de plantillas son formas simples de alterar el valor de una variable antes de mostrarla.3. o booleanos. listas. EL SISTEMA DE PLANTILLAS DE DJANGO Como { % if %}. {% {% {% {% en Sólo las variables de la plantilla. Un comentario no puede abarcar múltiples líneas.

4. Vemos al sistema de plantillas como una herramienta que controla la presentación y la lógica relacionado a esta -. escape & < > " ' en en en hace estas conversiones: • • • • • Convierte Convierte Convierte Convierte Convierte &amp. comillas. tenlo en cuenta. &#39. Django espera las plantillas de los autores para estar cómodo editando HTML directamente. Aún así. junto con algunas losofías detrás de la forma en que este funciona.y eso es todo. como texto plano. Los diseñadores se supone que se sienten más cómodos con el código HTML. pero las etiquetas de Django intencionalmente no permiten ejecutar código arbitrario de Python.6. Es posible escribir etiquetas personalizadas que hagan cosas arbitrarias. Requerir un XML válido para escribir plantillas introduce un mundo de errores humanos y mensajes difícil de entender. (½De hecho. Como verás en la próxima sección  Uso de plantillas en las vistas. El sistema de plantillas no está diseñado para que las plantillas necesariamente sean mostradas de forma agradable en los editores WYSIWYG 3 tales como Dreamweaver. Algunos otros lenguajes de plantillas están basados en XML. revisión 789 del 9 de noviembre de 2009 . y corchetes del string tomado. sino cientos. pero no es un requerimiento estricto en ningún sentido. (comilla doble) en (comilla simple) en length: Retorna la longitud del valor. El sistema de plantillas tiene raíces en la forma en que el desarrollo web se realiza en World Online y la experiencia combinada de los creadores de Django. debemos señalar algunas de sus limitaciones intencionales. Éstas con algunas de esas losofías: La lógica de negocios debe ser separada de la presentación lógica. &gt. La sintaxis debe ser independiente de HTML/XML. Con eso en la cabeza. Especícamente. FILOSOFÍA Y LIMITACIONES 37 date: Formatea un objeto date o datetime de acuerdo al formato tomado como parámetro. &quot. quizás a veces es más conveniente usar el sistema de plantillas de Django que otras bibliotecas de plantillas de Python. Eso es también una limitación severa y no permitiría que la sintaxis sea tan clara como lo es. es muy fácil usar otro lenguaje de plantillas con Django. se dice que es un rito para los desarrolladores de Python escribir su propio lenguaje de plantillas! Si todavía no lo has hecho. Más que cualquier otro componente de la aplicación web. de lenguajes de plantillas de código abierto lo dice todo. El sistema de plantillas no debería admitir funcionalidad que vaya más allá de este concepto básico. escape: Escapa ampersands(&). las opiniones de los programadores sobre el sistema de plantillas varía extremadamente. 6. Es un ejercicio divertido). Aunque el sistemas de plantillas de Django es usado principalmente para producir HTML. Pero Django pretende ser un completo framework que provee todas las piezas necesarias para que el desarrollo web sea productivo. este pretende ser útil para formatos no HTML. Puedes usar este con una lista o con un string. Por esta razón. Y" }} El formato de los strings está denido en el Apéndice F. es imposible llamar a código Python directamente dentro de las plantillas de Django. debes estar interesado en saber que Django no requiere que uses su lenguaje de plantillas. Filosofía y Limitaciones Ahora que tienes una idea del lenguaje de plantillas de Django. El hecho de que Python sólo implemente decenas. Esto es usado para desin- fectar datos suministrados por el usuario y asegurar que los datos son válidos para XML y XHTML. por ejemplo: {{ pub_date|date:"F j. es claro que tenemos una fuerte preferencia por el sistema de plantillas de Django. pero Django evita deliberadamente esta limitación. y usando un motor de XML para parsear plantillas implica un inaceptable nivel de overhead en el procesamiento de la plantilla. Todo programador está fundamentalmente limitado al alcance de lo que una etiqueta puede hacer.4. Cada uno fue creado probablemente porque su desarrollador estima que todos los existentes son inadecuados. &lt. poniendo toda la lógica de plantilla con etiquetas XML o atributos. o con cualquier objeto Python que sepa como determinar su longitud (o sea cualquier objeto que tenga el método __len__()).

De nuevo. El objetivo no es inventar un lenguaje de programación. 6. pero la pila de etiquetas de Django no lo permiten. # This doesn't account for missing files! revisión 789 del 9 de noviembre de 2009 . Vamos a solucionar esto poniendo la plantilla en un archivo separado.now() html = "<html><body>It is now %s. y por esto no debería asumir ningún conocimiento de Python. Esto ofrece otro camino para extender la sintaxis del sistema escribiendo código Python puro. Puedes primer considerar guardar la plantilla en algún lugar del disco y usar las funcionalidades de Python para abrir y leer el contenido de la plantilla. esto usa el sistema de plantillas.template import Template.http import HttpResponse import datetime def current_datetime(request): now = datetime. podemos pensar en algo como esto: from django.datetime. pero no soluciona el problema que planteamos en la introducción de este capítulo. Sin embargo. la que comenzamos en el capítulo anterior. No hay forma de ingresar en modo Python o usar sentencias puras de Python. Primero. el lenguaje de plantillas de Django tiene las siguientes limitaciones: Una plantilla no puede asignar una variable o cambiar el valor de esta. Se veía como from django.</body></html>") html = t. pero la pila de etiquetas Una plantilla no puede llamar código Python crudo. El objetivo es ofrecer sólo la suciente funcionalidad de programación. el sistema también pretende acomodar pequeños grupos en los cuales las plantillas sean creadas por programadores de Python.datetime. suponiendo que la plantilla esté guardada en /home/djangouser/templates/mytemplate.datetime.5.http import HttpResponse import datetime def current_datetime(request): now = datetime.html: from django. que la vista cargará. EL SISTEMA DE PLANTILLAS DE DJANGO Se supone que los diseñadores no son programadores Python.now() t = Template("<html><body>It is now {{ current_date }}. Como resultado de esta losofía. Esto es posible escribiendo una etiqueta personalizada para cumplir con esta meta (ve el de Django no lo permite.views. no por programadores.now() # Simple way of using templates from the filesystem. que son esenciales para hacer presentaciones relacionadas a decisiones.38 CAPÍTULO 6. Context from django. (Más de esto en el `Capítulo 10`_).render(Context({'current_date': now})) return HttpResponse(html) Seguro. Uso de plantillas en las vistas current_datetime en Has aprendido el uso básico del sistema de plantillas. Context from django. esto es posible creando plantillas personalizadas.</body></html>" % now return HttpResponse(html) Vamos a cambiar esta vista usando el sistema de plantillas de Django. El sistema de plantillas de los autores reconoce que las plantillas de las páginas web son en al mayoría de los casos escritos por diseñadores. ahora vamos a usar este conocimiento para crear una vista. tales como ramicación e iteración. la plantilla sigue estando embebida en el código Python. A saber.template import Template. Recordemos la vista esto: mysite.http import HttpResponse import datetime def current_datetime(request): now = datetime. Esto puede verse así. `Capítulo 10`_).

te recomendamos crear un directorio del proyecto de Django (esto es. dentro del directorio siguiendo los ejemplos a lo largo del libro). los cuales son descriptos 6. TEMPLATE_DIRS = ( '/home/django/mysite/templates'. Si quieres evitar este error. El lugar para hacer esto es en el archivo de conguración. pero por ahora. Esto es común en los usuarios nuevos. y fp.html') t = Template(fp.close() html = t. Para usar la API para cargar plantillas. si vienes TEMPLATE_DIRS una lista. Es un simple módulo de Python con variables. puedes hacer mysite templates dentro que creaste en el Capítulo 2. Cargadores de plantillas 4 para cargar plantillas del disco. Por omisión. las conguraciones y sus respectivos valores son simples variables de Python. así: Esta variable le indica al mecanismo de carga de plantillas dónde buscar las plantillas. Como el archivo de conguración es sólo un módulo plano de Python. el script creó un archivo de consettings. Échale un vistazo al contenido del archivo.6. (Esto también signica que debes evitar errores de sintaxis de Python en los archivos de conguración). veamos la variable de conguración TEMPLATE_DIRS. primero necesitas indicarle al framework dónde están guardadas tus plantillas. una por cada conguración. estarás duplicando rutas de plantillas.urls' Éstas se explican por sí solas.read() en las secciones que siguen. sin embargo.6. Si vas a usar esta técnica para cada una de las funciones de las vistas. Si no puedes pensar en un lugar apropiado para poner las plantillas. ½No olvides la coma al nal del string del directorio de plantillas! Python requiere una coma en las tuplas de un solo elemento para diferenciarlas de una expresión de paréntesis.close() cada vez que cargas una plantilla Para solucionar estos problemas. porque un solo elemento en una lista no requiere estar seguido de una coma: revisión 789 del 9 de noviembre de 2009 . Tienes mejores cosas para hacer en vez de escribir open(). ésta es una tupla vacía. Cubriremos el archivo de conguración en profundidad en el Apéndice E.render(Context({'current_date': now})) return HttpResponse(html) Esta aproximación. fp. Cuando ejecutaste guración por omisión por ti.6. ) Hay algunas cosas para notar: Puedes especicar cualquier directorio que quieras. no existe o no es Involucra la ruta de tu plantilla. siempre y cuando la cuenta de usuario en el cual se ejecuta el servidor web tengan acceso al directorio y su contenido. es poco elegante por estas razones: No maneja el caso en que no encuentre el archivo.read()) fp. El archivo de conguración de Django es el lugar para poner conguraciones para tu instancia de Django (aka tu proyecto de Django). CARGADORES DE PLANTILLAS 39 fp = open('/home/djangouser/templates/mytemplate. con el objetivo de quitar la redun- Django provee una práctica y poderosa API dancia en la carga de la plantilla y en las mismas plantillas. Elige un directorio en el que desees guardar tus plantillas y agrega este a TEMPLATE_DIRS.html IOError.py startproject mysite en el Capítulo 2. la llamada a open() levantará la excepción mytemplate. usamos cargadores de plantillas y directorios de plantillas. puedes hacer cosas dinámicas como vericar el valor de una variable antes de congurar otra. Este contiene variables que se parecen a estas (no necesariamente en este orden): DEBUG = True TIME_ZONE = 'America/Chicago' USE_I18N = True ROOT_URLCONF = 'mysite. ½Sin mencionar que esto implica teclear mucho más! Incluye una cantidad aburrida de código repetitivo. Si el archivo accesible para lectura.py. bien llamado 5 django-admin. en vez de una tupla.

py runserver en el directorio de tu proyecto de Django. import os.render(Context({'current_date': now})) return HttpResponse(html) el sistemas de archivos manualmente. EL SISTEMA DE PLANTILLAS DE DJANGO TEMPLATE_DIRS = [ '/home/django/mysite/templates' ] Una tupla es un poco más correcta semánticamente que una lista (las tuplas no pueden cambiar luego de ser creadas.html') html = t. para no incluir la ruta a la plantilla. y retorna un objeto Template compilado.dirname(__file__).http import HttpResponse import datetime def current_datetime(request): now = datetime. Django intentó buscar una plantilla combinando el directorio de la variable tonces si tu variable Si TEMPLATE_DIRS TEMPLATE_DIRS con el nombre de la plantilla pasada a get_template(). File does not exist). ) Es más sencillo usar rutas absolutas (esto es. puedes tomar el hecho de que el archivo de conguración de Django es sólo código de Python y construir la variable TEMPLATE_DIRS dinámicamente. lo abre.</body></html> Refresca la página en tu navegador web.join(os. EnTEMPLATE_DIRS contiene '/home/django/templates'. se cuenta de dónde está la plantilla en el sistema de archivos. Esta sección te indica qué plantilla intentó cargar Django acompañado de una razón para cada intento fallido (por ej.html.path por ejemplo: TEMPLATE_DIRS = ( os. ejecutando python manage. http://127. y deberías ver la página completamente renderizada.template import Context from django. usamos la función Para ver que cómo se ve eso.40 CAPÍTULO 6. las rutas de directorios comienzan desde la raíz del sistema de archivos). revisión 789 del 9 de noviembre de 2009 .path. Asumiendo que tu variable de conguración DEBUG está asignada a True y todavía no has creado la plantilla current_datetime. Si get_template() no puede encontrar la plantilla con el nombre pasado. el próximo paso es cambiar el código de vista que usa la funcionalidad current_datetime. y nada podría cambiar las conguraciones una vez que fueron leídas).loader import get_template from django. from django. Luego.loader. Continuando. Con la variable al de carga de plantillas de Django. la cual es automáticamente asignada nombre del archivo del módulo de Python en el que se encuentra el código. ) Este ejemplo usa la variable de Python mágica __file__. En este ejemplo.template. también. Django buscará '/home/django/templates/current_da current_datetime.datetime.0.'/'). Volvamos a nuestra vista vamos a cambiar esta como sigue: TEMPLATE_DIRS congurada. Si quieres sen un poco más exible e independiente. Como probablemente puedas distinguir de los mensajes de error de la Figura 4-1. 'templates'). La función da django.1:8000/time/). incluye tu letra de unidad y usa el estilo de Unix para las barras en vez de barras invertidas. Esta información es invaluable cuando hacemos depuración de errores de carga de plantillas.template.now() t = get_template('current_datetime.get_template() en vez de cargar la plantilla desde get_template() toma el nombre de la plantilla como argumento.html en tu directorio de plantillas usando el siguiente código: contiene más que un directorio.replace('\\'. como en el Capítulo 3.path. como sigue: TEMPLATE_DIRS = ( 'C:/www/django/templates'. esta levanta una excepción TemplateDoesNotExist. deberías ver una página de error de Django resaltando el error TemplateDoesNotExist. Si estás en Windows. ejecutar el servidor de desarrollo de Django otra vez. Esta página de error es similar a la que explicamos en el Capítulo 3. escribe en tu navegador la página que activa la vista current_datetime (o sea. nosotros recomendamos usar tuplas para la variable TEMPLATE_DIRS. con una pieza adicional de información de depuración: una sección Postmortem del cargador de plantillas. crea el archivo <html><body>It is now {{ current_date }}.0. cada uno de estos es examinado hasta que se encuentre la plantilla o hasta que no haya más directorios.

CARGADORES DE PLANTILLAS 41 Figura 6.6.1: La página de error que se muestra cuando una plantilla no se encuentra revisión 789 del 9 de noviembre de 2009 .6.

o HttpResponse. De esta manera. Esta retorna un diccionario mapeando todos los nombres de variables locales con sus valores.datetime. En el ejemplo anterior. En vez django. porque cuando es llamado. locals(). puedes tomar la ventaja de la función built-in de Python llamada locals(). creación HttpResponse se encarga la llamada a render_to_response() retorna un objeto HttpResponse. HttpResponse render_to_response(). revisión 789 del 9 de noviembre de 2009 .html'. renombramos locals() now a current_date. Como una consecuencia. Si especicas el diccionario al contexto manualmente.html'. mayoría de las veces. import datetime se mantiene. renderización de esta. buscarás tú mismo calcular algunos valores. porque esta es la variable que especicamos en la plantilla.html'. now Muchas veces.42 CAPÍTULO 6. Particularmente los programadores perezosos notarán que es ligeramente redundante tener esos nombres en variables temporales y tener nombres para las variables de la plantilla. locals() también incluirá request. usarás manualmente. pero esta técnica puede ahorrar un poco de tipeo si tienes plantillas con varias variables denidas -. Context para esa plantilla. sino que también hay que teclear más. el cual incluye todas las variables denidas hasta ese punto en la ejecución de la función. Context.render_to_response. current_datetime. El segundo argumento.shortcuts. con lo cual quizás conste de más variables de las cuales quieres tener acceso en la plantilla. Template.6. rellenar un Este atajo es la función llamada Context. Depende de tu aplicación saber si esto es de importancia. podemos en la vista.now() return render_to_response('current_datetime. La render_to_response() en vez de cargar las plantillas y crear los objetos Context y current_datetime reescrito utilizando Aquí está el ejemplo actual render_to_response(): from django. Entonces si eres uno de esos programadores perezosos y quieres ahorrar código particularmente conciso. evitas esta sobrecarga. guardando ellos en variables (por en el código anterior). y de la creación de simplemente render_to_response(). Una cosa en la que tiene que tener cuidado cuando usas locals() es que esta incluye todas las variables locales. locals()) Aquí. El truco locals() Considera nuestra última versión de current_datetime: def current_datetime(request): now = datetime. No sólo que esto es redundante. Como retornar ese valor now. pasamos el valor de el nombre de la variable ejemplo. la vista anterior podría reescribirse como sigue: def current_datetime(request): current_date = datetime.1. EL SISTEMA DE PLANTILLAS DE DJANGO 6.now() return render_to_response('current_datetime. {'current_date': now}) ej. Python crea el diccionario dinámicamente. La última cosa a considerar es que locals() provoca un poco sobrecarga. {'current_date': now}) ½Qué diferencia! Vamos paso a paso a través de los cambios del código: No tenemos que importar portamos get_template. pero la carga de la plantilla. render_to_response() Debido a que es común cargar una plantilla. El primer argumento de segundo argumento.2.6.shortcuts. debe ser un diccionario para usar en la creación de un render_to_response() debe ser el nombre de la plantilla a utilizar. en vez de especicar manualmente el diccionario al contexto como antes. si es pasado. como en este ejemplo. En este no ofrece una gran mejora.shortcuts import render_to_response import datetime def current_datetime(request): now = datetime. im- En la función del contexto. Django provee un atajo que te deja hacer estas cosas en una línea de código.now() return render_to_response('current_datetime. seguimos calculando de esto.datetime. y pasando estas a la plantilla.datetime. 6. y retornar un objeto HttpResponse con el resultado de la plantilla renderizada. la cual se encuentra en el módulo django. Si no se le pasa un render_to_response() utilizará un diccionario vacío.o si eres perezoso.

Esta etiqueta te permite incluir el contenido de otra plantilla. considera utilizar un { % include %} para eliminar lo duplicado. usarás el sistema de plantillas de Django para crear páginas HTML enteras. En esencia. editando el revisión 789 del 9 de noviembre de 2009 . así: get_template(). Veamos un ejemplo de esto creando una plantilla completa para nuestra vista archivo current_datetime. Si una plantilla no encuentra el nombre tomado. El argumento para esta etiqueta debería ser el nombre de la plantilla a incluir. insertando dentro de las páginas HTML a incluir una página dentro de otra.html: current_datetime. La etiqueta de plantilla include Ahora que vimos el mecanismo para cargar plantillas. Siéntete libre de usar tantos como quieras. HERENCIA DE PLANTILLAS 43 6. De hecho.4.html" %} Este ejemplo incluye el contenido de la plantilla includes/nav. y esto está bien. 6. puedes hacer lo mismo con el render_to_response(). Esto conduce a un problema común del desarrollo web: ¾Cómo reducimos la duplicación y redundancia de las áreas comunes de las páginas. sin mostrar nada en el de error de Django.7. las cuales veremos en el Capítulo 9) esperan esta distribución de las plantillas como una convención por omisión. y el nombre de la plantilla puede ser una variable string hard-coded (entre comillas). con la etiqueta { % include %} anteriormente descripta. get_template() 6. Guardar las plantillas en subdirectorios de tu directorio de plantilla es fácil. recomendamos hacerlo. Pero la mejor forma de solucionar este problema con Django es usar una estrategia más elegante llamada herencia de plantillas. Nota Los usuario de Windows. t = get_template('dateapp/current_datetime.html: template_name: { % include 'includes/nav.7. lugar de la etiqueta.html. asume el estilo de designación de archivos de Unix. Las plantillas incluidas son evaluadas con el contexto de la plantilla en la cual está incluida. verás la excepción TemplateDoesNotExist sobre la página False. Es más.html') Debido a que primer argumento de render_to_response() es un pequeño envoltorio de get_template(). Django admite esta aproximación.html' %} Este ejemplo incluye el contenido de la plantilla cuyo nombre se encuentra en la variable { % include template_name %} Como en tomado de get_template(). el nombre del archivo de la plantilla es determinado agregando el directorio de plantillas TEMPLATE_DIRS para el nombre de plantilla solicitado. Quizás quieras guardar las plantillas en subdirectorios del directorio de tus plantillas. Estos dos ejemplos incluyen el contenido de la plantilla cualquier modo de comillas está permitido: nav. pero en el mundo real. asegúrense de usar barras comunes en vez de barras invertidas.6. Herencia de plantillas Nuestras plantillas de ejemplo hasta el momento han sido fragmentos de HTML. la herencia de plantillas te deja construir una plantilla base esqueleto que contenga todas las partes comunes de tu sitio y denir bloques que los hijos puedan sobreescribir. Los ejemplos son equivalentes e ilustran que { % include 'nav.3.html' %} { % include "nav. algunas de las características más avanzadas de Django (como las vistas genéricas del sistema. No hay límites para la profundidad del árbol de subdirectorios. los paneles de navegación? Una forma clásica de solucionar este problema es usar includes. En tus llamadas a sólo incluye el nombre del subdirectorio y una barra antes del nombre de la plantilla. Django hará una de estas dos cosas: Si Si DEBUG DEBUG es es True. la etiqueta fallará silenciosamente.6. entre simples o dobles comillas. Subdirectorios en get_template() Puede ser un poco inmanejable guardar todas las plantillas en un solo directorio.6. podemos introducir una plantilla built-in que tiene una ventaja para esto: { % include %}. como por ejemplo. En cualquier momento que tengas el mismo código en varias etiquetas.

un esquelete de tu página que las plantillas hijas llenaran luego. Si incluimos <h1> incluir <title>.pero header.01//EN"> <html lang="en"> <head> <title>Future time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>In {{ hour_offset }} hour(s). ¾Ves a dónde <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4. que luego son incluidos en cada plantilla.</p> <hr> <p>Thanks for visiting my site.44 CAPÍTULO 6. quizás algo de JavaScript -. Quizás quieras guardar la parte superior de la plantilla en un archivo llamado header.</p> </body> </html> hours_ahead Esto se ve bien.</p> </body> </html> Claramente. Aquí hay una platilla para nuestro ejemplo actual: <h1>My helpful timestamp site</h1>-. Lo puedes pensar a esto como la versión contraria a la del lado del servidor.terminaríamos poniendo todo tipo de HTML redundante en cada plantilla. estaríamos duplicando una cantidad de código HTML.01//EN"> revisión 789 del 9 de noviembre de 2009 . incluyendo barra de navegación. En vez de denir los pedazos que son comunes. ambas páginas contienen un título -ese título no puede encajar dentro de en la cabecera. EL SISTEMA DE PLANTILLAS DE DJANGO <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4. tendríamos que queremos llegar? El sistema de herencia de Django soluciona estos problemas.html porque <title> en las dos páginas es diferente. Es el medio el que queda desordenado. válida. En este ejemplo. La solución a este problema usando includes en el servidor es sacar factor común de ambas plantillas y guardarlas en recortes de plantillas separados. Imagina si tendríamos más sitios típicos. denes los pedazos que son diferentes.html: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>The current time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>It is now {{ current_date }}. lo cual no permitiría personalizar este en cada página. pero ¾Qué sucede cuando queremos crear una plantilla para otra vista --digamos. la cabecera y la parte de abajo son fáciles. y completa plantilla HTML.01//EN"> <html lang="en"> <head> Y quizás quieras guardar la parte inferior en un archivo llamado footer. ¾La vista del Capítulo 3? Si queremos hacer nuevamente una agradable. it will be {{ next_time }}. El primer paso es denir una plantilla base -.</p> <hr> <p>Thanks for visiting my site. algunas hojas de estilo.html: <hr> <p>Thanks for visiting my site. crearíamos algo como: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.</p> </body> </html> Con una estrategia basada en includes.

html base_forum. te dejamos cambiar hours_ahead del Capítulo 3. Ahora que tenemos una plantilla base. el motor de plantillas ve la etiqueta { % block %} en base. sólo cambia el efecto inmediatamente.</p> { % endblock %} </body> </html> Esta plantilla. Crear una plantilla y estilo/diseño. base_photos. Es trabajo de las plantillas hijas sobreescribir. podemos modicar nuestra plantilla existente usar esto: current_datetime.6.html. el título que denimos en { % block content %}.html.7. it will be {{ next_time }}.html e incluyen secciones especícas de . agregar. (Si lo estás hours_ahead para usar el sistema de plantilla).html.html para { % extends "base.html). y todas las otras plantillas reejarán { % extends %}. el motor de la plantilla nota las tres etiquetas bloques por el contenido de la plantilla hija. Todas las etiquetas {% le indican al motor de plantillas que una plantilla hijo quizás sobreescriba esa porción de la plantilla. revisión 789 del 9 de noviembre de 2009 base_SECTION. Esas plantillas heredan de base. que llamamos base.html.html que contenga el aspecto principal de tu sitio. dejar vacío el contenido de los bloques. guarda este archivo en tu directorio de plantillas).html" %} { % block title %}The current time{ % endblock %} { % block content %} <p>It is now {{ current_date }}. Una forma común de utilizar la herencia es el siguiente enfoque de tres niveles: 1. dene un documento esqueleto HTML simple que usaremos para todas las páginas del sitio. Si necesitas hacer un cambio grande en el diseño del sitio. Crear una plantilla base. 2. (Si estás lo siguiendo desde casa. El contenido de la etiqueta alternativo. HERENCIA DE PLANTILLAS 45 <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. Entonces. Así sería el resultado: { % extends "base. Veamos cómo trabaja. Nota que desde la plantilla hija no denimos el bloque la plantilla padre.</p> { % endblock %} ¾No es hermoso? Cada plantilla contiene sólo el código que es único para esa plantilla. y puedes usar tantos niveles de herencia como necesites. entonces el sistema de plantillas usa el valor desde { % block %} en la plantilla padre es siempre usado como un plan La herencia no afecta el funcionamiento del contexto.</p> { % endblock %} Como estamos en este tema. nota caso. El motor inmediatamente carga la plantilla padre --en este Hasta este punto.html" %} { % block title %}Future time{ % endblock %} { % block content %} <p>In {{ hour_offset }} hour(s). No necesita redundancia. si es que alguna vez cambia. Esto es lo que rara vez cambiará. como current_datetime. footer. Cuando cargamos una plantilla base. así que esta plantilla es la hija de otra.html para cada sección de tu sitio (por ej.html y reemplaza estos { % block title %} será usado. vamos a crear una plantilla para la vista siguiendo junto con el código. base. Usamos una etiqueta de plantilla aquí que no hemos visto antes: la etiqueta block %} { % block %}.

sino que también dene el contenido que llenará ese agujero en el padre. EL SISTEMA DE PLANTILLAS DE DJANGO 3. si no sabes el nombre de la plantilla padre hasta la ejecución. mejor.super }} hará este truco. En otro caso. Esto te permite hacer 6. entonces puedes rellenar un número razonable de bloques por omisión. 2 N. Esto es. cuanto más etiquetas { % block %} tengas en tus plantillas.: WYSIWYG: What you see is what you get (Lo que ves es lo que obtienes) del T. Esta limitación existe porque una etiqueta bloque trabaja en ambas direcciones. Esto es útil si quieres agregar contenido del bloque padre en vez de sobreescribirlo Si necesitas obtener el contenido de un bloque desde la plantilla padre. { % extends %} será un string. Crear una plantilla individual para cada tipo de página.: hard-coded del T. el nombre de la plantilla es agregado a la variable En la mayoría de los casos. como puede ser un navegador de sección. Es mejor tener más conexiones que menos. Generalmente. Recuerda. Si hay dos nombres similares de etiquetas plantilla puede no saber cual de los bloques usar.46 CAPÍTULO 6. Aquí hay algunos consejos para el trabajo con herencia de plantillas: Si usas { % extends %} en la plantilla. El nombre de plantilla pasado a { % block %} en una plantilla. { % extends %} es cargado usando el mismo método que get_template(). 3 N. esto probablemente signique que debes mover ese código a un { % block %} en la plantilla padre. la variable completamente. 5 N. Esta aproximación maximiza la reutilización de código y hace fácil el agregado de elementos para compartir áreas. la herencia no funcionará. No puedes denir múltiples etiquetas { % block %} con el mismo nombre en la misma plantilla.: API: Application Program Interface (Interfaz de programación de aplicaciones) del T. del T.: aka: Also Know As (También conocido como) revisión 789 del 9 de noviembre de 2009 . 4 N. Estas plantillas heredan de la plantilla de la sección apropiada. Si encuentras código duplicado en un número de plantillas. tales como páginas de formulario o galería de fotos. dinámicas.8. el argumento para cosas divertidas. TEMPLATE_DIRS. Esto permite una clara separación de los datos y la lógica (de la misma manera que las vistas y las etiquetas permiten una separación de la lógica y la vista). pero también puede ser una variable. {{ block. ¾Qué sigue? Los sitios web más modernos son manejados con una base de datos : el contenido de la página web está guardado en una base de datos relacional. el padre de esta Esto es. una etiqueta bloque no sólo provee un agujero a llenar. y luego denir sólo lo que necesiten las plantillas hijas. El `próximo capítulo`_ cubre las herramientas que Django brinda para interactuar con la base de datos. esta debe ser la primer etiqueta de esa plantilla. las plan- tillas hijas no tienen que denir todos los bloques del padre. Duplicate explicit target name: próximo capítulo.

por ejemplo.com. pero continúa leyendo si eres nuevo en el tema. En el ejemplo. es un gran ejemplo de un sitio que maneja una base de datos. nuestra lógica arbitraria era calcular la fecha y hora actual. En las aplicaciones web modernas. (Nota: Aunque no es estrictamente necesario conocer teoría básica de bases de datos y SQL para usar la capa de base de datos de Django. Como explicamos. db='mydb'. la lógica arbitraria a menudo implica interactuar con una base de datos.Capítulo 7 Interactuar con una base de datos: Modelos En el Capítulo 3. y cuando envías una opinión de cliente (customer review ). el sitio puede proporcionar funcionalidad que permita a los visitantes del sitio poblar la base de datos por su propia cuenta. En este ejemplo de vista. ya que incluye una manera fácil pero poderosa de realizar consultas a bases de datos utilizando Python.cursor() cursor. recuperar algunos registros. recupera algunos datos de esta. Amazon. usamos la biblioteca mostrar una página web: MySQLdb (disponible en http://www.connect(user='me'. La manera tonta de hacer una consulta a la base de datos en las vistas Así como en el Capítulo 3 detallamos la manera tonta de producir una salida con la vista (codicando en duro ) el texto directamente dentro de la vista). Muchos sitios web más complejos proporcionan alguna combinación de las dos. esta es insertada en la base de datos de opiniones. Detrás de escena. O. es altamente recomendado. Este capítulo explica esta funcionalidad: la capa de la base de datos de Django. Una introducción a estos conceptos está más allá del alcance de este libro. y luego retornar una respuesta. del mismo modo. un sitio web impulsado por una base de datos se conecta a un servidor de base de datos. 7. recorrimos los conceptos fundamentales de la construcción dinámica de sitios web con Django: La conguración de vistas y URLconfs. host='localhost') cursor = db. Cada página de un producto es esencialmente una consulta a la base de datos de productos de Amazon formateada en HTML.com/r/python-mysql/ para conectarnos a una base de datos de MySQL. Probablemente seas capaz de seguir adelante y captar los conceptos básicos en base al contexto). y los muestra con un formato agradable en una página web.shortcuts import render_to_response import MySQLdb def book_list(request): db = MySQLdb. hay una manera tonta de recuperar datos desde la base de datos en una vista. {'names': names}) Este enfoque funciona. Django es apropiado para crear sitios web que manejen una base de datos.fetchall()] db.html'. passwd='secret'. Esto es simple: sólo usa una biblioteca de Python existente para ejecutar una consulta SQL y haz algo con los resultados.1.djangoproject. una vista es responsable de implementar alguna lógica arbitraria.close() return render_to_response('book_list.execute('SELECT name FROM books ORDER BY name') names = [row[0] for row in cursor. y alimentar con ellos una plantilla para from django. pero deberían hacerse evidentes inmediatamente algunos problemas: revisión 789 del 9 de noviembre de 2009 .

books. tengamos sólo una idea de cómo es. Si. {'books': books}) Explicaremos este código enseguida en este capítulo. ejecutar una sentencia. Django fue diseñado para promover el acoplamiento débil y la estricta separación entre las piezas de una aplicación. Este es un adelanto de cómo la vista anterior puede ser reescrita usando la API de Django: from django.48 CAPÍTULO 7. Debido a que la C es manejada por el mismo framework y la parte más emocionante se produce en los modelos. En lugar de tener que decir a tus compañeros de trabajo. revisión 789 del 9 de noviembre de 2009 . Lo ideal sería que esos parámetros se guardarsen en la conguración de Django. 7. es manejada por la capa de la base de datos de Django. la porción de acceso a la base de datos. un cursor. y el Controlador implica la parte del sistema que decide qué vista usar. En las funciones de vista. la porción que selecciona qué datos mostrar y cómo mostrarlos. es manejada por el framework mismo siguiendo tu URLconf y llamando a la función apropiada de Python para la URL obtenida. luego vamos a tener una capa que se encarga de mostrar los datos.posiblemente reescribir el SQL. y la lógica de presentación -. V y C se separan en Django de la siguiente manera: M.html'. Si sigues esta losofía. la Vista se reere a la parte del sistema que selecciona qué mostrar y cómo mostrarlo. puedes sacar provecho de un vocabulario compartido y decir. Por ahora. la capa de la base de datos de Django apunta a resolver estos problemas. Con la capa de la base de datos. Lo ideal sería que todo lo que tuviéramos que hacer fuera especicar los resultados que queremos. Como mencionamos en los capítulos anteriores. Estas tres piezas juntas -.la lógica de acceso a la base de datos.order_by('name') return render_to_response('book_list. en el camino.2. Someramente. ¾Por qué el acrónimo? El objetivo de denir en forma explícita patrones como MVC es principalmente simplicar la comunicación entre los desarrolladores. C. es fácil hacer cambios en un lugar particular de la aplicación sin afectar otras piezas. y cerrar la conexión. Django es conocido como un Framework MTV. En el patrón de diseño MTV. En este patrón. Nos ata a MySQL. V. la M. Como esperabas. Vamos a usar un patrón MVC aquí. INTERACTUAR CON UNA BASE DE DATOS: MODELOS Estamos codicando en duro (hard-coding ) los parámetros de la conexión a la base de datos.comprenden un concepto que a veces es llamado el patrón de arquitectura de software Modelo-Vista-Controlador (MVC). La idea es que el servidor de base de datos que usemos esté abstraído. cambiamos de MySQL a PostgreSQL.objects. discutimos la importancia de separar la lógica de negocios de la lógica de presentación usando un sistema de plantillas. y vamos a poner una capa en el medio para que regule esto. accediendo al modelo si es necesario. tomémonos un momento para considerar el diseño global de una aplicación Web Django impulsada por bases de datos.models import Book def book_list(request): books = Book. Tenemos que escribir una cantidad de código estereotípico: crear una conexión. El patrón de diseño MTV Antes de profundizar en más código. Vamos a hacer una abstracción del acceso a la base de datos. entonces el pasarnos a otro servidor podría signicar realizar un cambio en un único lugar. las plantillas y las vistas. la porción que delega a la vista dependiendo de la entrada del usuario. la lógica de negocios.dependiendo de la naturaleza de las sentencia de SQL -. psycopg en vez de MySQLdb). aplicamos esa misma losofía para el acceso lógico a los datos. es manejada por la vista y las plantillas. la cual describiremos en este capítulo. alterar los parámetros de conexión y -. dependiendo de la entrada del usuario. Django sigue el patrón MVC tan al pie de la letra que puede ser llamado un framework MVC. por ejemplo. tenemos que usar un adaptador de base de datos diferente (por ej.shortcuts import render_to_response from mysite. el Modelo hace referencia al acceso a la capa de datos.

por omisión. y has creado una base de datos en este (por ej.0. llamado. http://www. cuál es el comportamiento que tiene. y las relaciones entre los datos. quizás consideres que las vistas de Django pueden ser el controlador y las plantillas de Django pueden ser la vista. usando la sentencia Como con datos: CREATE DATABASE). psycopg versión 2. http://www. DATABASE_ENGINE debe congurarse con un string de los mostrados en la Tabla 5-1. adodbapi version 2.x. revisión 789 del 9 de noviembre de 2009 . la vista describe los datos que son presentados al usuario. Conguración de la base de datos Con toda esta losofía en mente.com/ r/python-pgsql/. Esta capa contiene la lógica que accede al modelo y la delega a la plantilla apropiada: puedes pensar en esto como un puente entre el modelos y las plantillas. Lo importante es entender los conceptos subyacentes.x. no hay que crear una base de datos.7. necesitamos tener en cuenta algunas conguraciones iniciales: necesitamos indicarle a Django qué servidor de base de datos usar y cómo conectarse con el mismo. Esta capa contiene las decisiones relacionadas a la presentación: como algunas cosas son mostradas sobre una página web o otro tipo de documento.1+.djangoproject. Ruby on Rails y frameworks similares sugieren que el trabajo del controlador incluya la decisión de cuales datos son presentados al usuario. Primero.com/r/ python-mysql/. la capa de acceso a la base de datos. lo has activado.py. mientras que la vista sea estrictamente el cómo serán presentados y no cuáles. vamos a comenzar a explorar la capa de la base de datos de Django. DATABASE_ENGINE le indica a Django qué base de datos utilizar. 7. la capa de la lógica de negocios. Ninguna de las interpretaciones es más correcta que otras.djangoproject.djangoproject. MySQLdb. http://www.com/ r/python-sqlite/. En la interpretación de Django de MVC. SQLite es un caso especial. es este caso.djangoproject. com/r/python-ado/. la capa de presentación.5+. de conguración de Django. Esto es una confusión desafortunada a raíz de las diferentes interpretaciones de MVC. V signica View (Vista). porque SQLite usa un archivo autónomo sobre el sistema de archivos para guardar los datos.1: Conguración de motores de base de datos Conguración Base de datos PostgreSQL Adaptador requerido postgresql postgresql_psycopg2 mysql sqlite3 ado_mssql PostgreSQL MySQL psycopg version 1. no necesariamente el cómo se mostrarán. como Ruby on Rails. Si usas una base de datos con Django. Esta capa contiene toda la información sobre los datos: cómo acceder a estos. la conguración de la base de datos se encuentra en el archivo settings. CONFIGURACIÓN DE LA BASE DE DATOS 49 M signica Model (Modelo). Edita este archivo y busca las opciones de la base de DATABASE_ENGINE = '' DATABASE_NAME = '' DATABASE_USER = '' DATABASE_PASSWORD = '' DATABASE_HOST = '' DATABASE_PORT = '' Aquí hay un resumen de cada propiedad. pero si cuáles datos son presentados. http://www.djangoproject. No necesita adaptador si se usa Python 2. cómo validarlos.3. En contraste. contrario. TEMPLATE_DIRS en los capítulos anteriores.3. Si estás familiarizado con otros frameworks de desarrollo web MVC. En caso SQLite Microsoft SQL Server pysqlite. Cuadro 7. Asumimos que haz congurado un servidor de base de datos.com/ r/python-pgsql/1/. http://www. T signica Template (Plantilla).

DATABASE_PASSWORD le DATABASE_HOST indica a Django cual es la contraseña a utilizar cuando se conecte con tu base de datos. le indica a Django cual es el host a usar cuando se conecta a tu base de datos. Si estás utilizando SQLite. deja este en blanco.db'). En la mayoría de los casos. Notarás que comienza un intérprete interactivo de Python. Si estás usando SQLite. Detrás de escena. En otro caso.cursor() Si no sucede nada. MySQL se conectará al socket especicado por medio de un socket Unix. entonces este valor es asumido como el host.db import connection >>> cursor = connection.1: Conguración de motores de base de datos Conguración Base de datos Oracle Adaptador requerido oracle cx_Oracle. Primero. La Tabla 5-2 muestra algunos mensajes de error comunes. El último es el Python shell básico.py shell. python.py shell cuando necesites hacer modicaciones especícas >>> from django. directorio que tulo lo a Django. por ejemplo: DATABASE_HOST = '/var/run/mysql' Si estás utilizando MySQL y este valor no comienza con una barra.50 CAPÍTULO 7. por lo tanto puedes dejar este en blanco. DATABASE_NAME la indica a Django el nombre de tu base de datos. deja este en blanco. deja este en blanco.py shell simplemente asume que tu archivo de conguración está en el mismo manage. compruébalas. De lo contrario revisa el mensaje de error para obtener un indicio sobre qué es lo que está mal. deja este en blanco. Si estás usando SQLite. el adaptador de base de datos subyacente usará el puerto por omisión acorde al servidor de base de datos.py shell dentro del directorio del proyecto de Django y el más python. Hay otras maneras de indicarle a Django qué módulo de conguración usar.com/r/ python-oracle/. Por ahora. http://www. pero este subtícubriremos luego.py shell en vez revisión 789 del 9 de noviembre de 2009 .py.2: Mensajes de error de conguración de la base de datos Mensaje de error You haven't set the DATABASE_ENGINE setting yet. es- pecica la ruta completo del sistema de archivos hacia el archivo de la base de datos (por ej. sólo sigue el enlace en la columna Adaptador requerido en la Tabla 5-1. deja este en blanco. Este es un requerimiento clave para hacer consultas a la base de datos: Django necesita saber cuales son los archivos de conguraciones a usar para obtener la información de la conexión a la base de datos. necesitarás descargar e instalar el adaptador apropiado. Cada uno de estos está disponible libremente en la web. Una vez que hayas ingresado estas conguraciones. DATABASE_PORT le indica a Django qué puerto usar cuando se conecte a la base de datos. Nota que cualquiera sea la base de datos que uses. '/home/django/mydata. Si estás utilizando SQLite o tienes una contraseña vacía. DATABASE_USER le indica a Django cual es el nombre de usuario a usar cuando se conecte con tu base de datos. Una vez que hayas entrado al shell. ejecuta el comando importante entre ejecutar el comando genérico python manage. Si tu base de datos está sobre la misma computadora que la instalación de Django (o sea localhost). entonces tu base de datos está congurada correctamente. Hay una diferencia python manage. desde el directorio del proyecto que creaste en el Capítulo 2. DATABASE_ENGINE con otra cosa Environment variable DJAN- Ejecuta el comando de GO_SETTINGS_MODULE is undened. el puerto por omisión está bien. si dejas este en blanco. INTERACTUAR CON UNA BASE DE DATOS: MODELOS Cuadro 7. Las apariencias pueden engañar. MySQL es un caso especial aquí. Si este valor comienza con una barra ('/') y estás usando MySQL. Si estás usando SQLite.djangoproject. python manage. Solución Congura la variable que un string vacío. pero el anterior le indica a Django cuales archivos de conguración usar antes de comenzar el shell. Cuadro 7. usa python manage. escribe estos comando para probar la conguración de tu base de datos: python manage.

py revisión 789 del 9 de noviembre de 2009 . Tu primera aplicación Ahora que vericamos que la conexión está funcionando.py views. pero crea un directorio vistazo al contenido: books dentro del directorio mysite. Vale la pena explicar la terminología aquí. Los modelos deben vivir dentro de aplicaciones. la variable TEMPLATE_DIRS. típicamente incluye modelos y vistas. existe un requisito respecto a la convención de la aplicación: si estás usando la capa de base de datos de Django (modelos). quizás uses una sola aplicación. más las conguraciones de esas aplicaciones. No se necesitan aplicaciones. y apuntamos nuestra URLconf a esa función. No obstante. que conviven en un solo paquete de Python. DATABASE_ENGINE con un motor válido descrito previamente. Cambia la variable datos. incluyendo modelos y vistas. porque esto es algo que suele hacer tropezar a los principiantes. DATABASE_USER para que apunte DATABASE_HOST a un usuario que exista.py. Técnicamente. Congura la variable ror de tipeo? psycopg o MySQLdb). es hora de crear una Aplicación de Django -. Hay pocas reglas estrictas sobre cómo encajar el código Django en este esquema. simplemente creamos un archivo llamado este con una función de vista. Una aplicación es un conjunto portable de una funcionalidad de Django. y DATABASE_PORT estén congurados correctamente y que el servidor esté 7. Echemos un books/ __init__. Solución No tienes instalado el módulo apropiado para la base de datos especicada (por ej. Si estás construyendo un sitio web complejo con varias piezas que no se relacionan entre sí. ¾Habrás cometido un er- database _____ does not exist Cambia la variable DATABASE_NAME para que apunte a una base de datos existente. o crea el usuario en tu base de could not connect to server Asegúrate de que corriendo. es exible. probablemente quieras dividir esto en aplicaciones para que te sea posible reusar estas individualmente en un futuro. tales como un sistema de comentarios y una interfaz de administración automática. Si estás construyendo un sitio web simple. ¾cuál es la diferencia entre un proyecto y una aplicación ? La diferencia es la que existe entre la conguración y el código: Un proyecto es una instancia de un cierto conjunto de aplicaciones de Django. debes crear una aplicación de Django. Por ejemplo. no necesariamente debes crear aplicaciones en absoluto.7. o ejecuta la sentencia CREATE DATABASE role _____ does not exist apropiada para crearla. _____ isn't an available database backend. en el Capítulo 2.4. el cual dene la información hacia la conexión a la base de datos. tales como un sistema de comercio electrónico o un foro. como lo hace evidente la función de la vista del ejemplo que creamos antes en este libro. escribe este comando para crear una nueva python manage. entonces. Dentro del directorio del proyecto aplicación llamada books: views. el único requerimiento de un proyecto es que este suministre un archivo de conguración. la lista de las aplicaciones instaladas.una colección de archivos de código fuente.4. Es más. En estos casos. llenamos mysite que creaste en el Capítulo 2. Django incluye un número de aplicaciones.py models. TU PRIMERA APLICACIÓN 51 Cuadro 7. Una cosa clave para notar sobre estas aplicaciones es que son portables y reusables en múltiples proyectos.2: Mensajes de error de conguración de la base de datos Mensaje de error Error loading _____ module: No module named _____. y así sucesivamente.py startapp books Este comando no produce ninguna salida. que conviven en un solo paquete de Python y representen una aplicación completa de Django. Ya hemos creado un proyecto.

Si haces cambios en un modelo Django. SQL permite sólo un cierto nivel de metadatos acerca de un *layout* de datos. por 7. Este es el espacio disponible para ser creativo con tu aplicación de Django.5. luego Python. Echa un vistazo a la importación en models. por ejemplo. Por otra parte. notablemente viejas versiones de MySQL. Django incluye una utilidad que puede generar modelos haciendo introspección sobre una base de datos existente. la M de MTV hace referencia al Modelo. Usamos esto como ejemplo porque las relaciones conceptuales entre libros. los desarrolladores de Django apuntan a quitar del framework tanto overhead como sea posible.lo equivalente de tu sentencia SQL CREATE TABLE -.6. por ejemplo. Teniendo que escribir SQL. De esta forma. Escribir Python es divertido. Tener modelos de datos guardados como código en vez de en tu base de datos hace fácil dejar tus modelos bajo un control de versiones. ¾No es redundante denir modelos de datos en Python y en SQL? Django trabaja de este modo por varias razones: La introspección requiere *overhead* y es imperfecta. y esta aproximación hace que Django sea más rápido que los frameworks competidores de alto nivel en mediciones de desempeño). no provee un tipo de datos especializado para representar una dirección web o de email. Esta es tu capa de datos -. o incluso cuando el servidor web sea inicializado. Tu primer modelo Como ejemplo continuo en este capítulo y el siguiente. es mucho más pragmático distribuir un módulo de Python que describa tu capa de datos que separar conjuntos de sentencias CREATE TABLE para MySQL. excepto models. Finalmente. esto podría provocar un nivel de overhead inaceptable. y dejar todo en Python limita el número de veces que tu cerebro tiene que realizar un cambio de contexto. Primero. necesitarás hacer los mismos cambios dentro de tu base de datos para mantenerla consistente con el modelo. Ambos archivos están vacíos. Si te mantienes en un solo entorno/mentalidad de programación tanto tiempo como sea posible. y la segunda sería la introspección de la base de datos en tiempo de ejecución para determinar el modelo de la base de datos. Con el objetivo de proveer una API conveniente de acceso a los datos. sin embargo. Segundo. SQL es inconsistente a través de distintas plataformas. Si estás familiarizado con base de datos. INTERACTUAR CON UNA BASE DE DATOS: MODELOS Esos archivos contendrán los modelos y las vistas para esta aplicación. Esto es útil para comenzar a trabajar rápidamente sobre datos heredados. representada como código de Python. La mayoría de sistemas de base de datos.py y views.excepto que están en Python en vez de SQL.py en tu editor de texto favorito. Si estás redistribuyendo una aplicación web. y es una conguración de datos comúnmente utilizada en libros de texto introductorios de SQL. Una contra de esta aproximación. nos enfocaremos en una conguración de datos básica sobre libro/autor/editor. puedes fácilmente dejar rastro de los cambios a tu capa de modelos. Los modelos de Django sí. Denir modelos en Python Como discutimos en los capítulos anteriores. no guardan suciente metadatos para asegurarse una completa introspección. La segunda forma parece clara. PostgreSQL y SQLite. introspeccionar una base de datos en tiempo de ejecución obviamente requiere overhead. es que es posible que el código Python quede fuera de sincronía respecto a lo que hay actualmente en la base. y luego SQL otra vez es perjudicial. algunas bases de datos. y hay dos formas de lograr esto. autores y editores son bien conocidas. Un modelo de Django es una descripción de los datos en la base de datos. porque los metadatos sobre tus tablas vive en un único lugar. inmediatamente podría pensar. Detallaremos algunas estrategias para manejar este problema más adelante en este capítulo. Django usa un modelo para ejecutar código SQL detrás de las escenas y retornar estructuras de datos convenientes en Python representando las las de tus tablas de la base de datos. La primera sería describir explícitamente los datos en Python. (Mientras algunos creen que el nivel de overhead es aceptable. 7. Django también usa modelos para representar conceptos de alto nivel que no necesariamente pueden ser manejados por SQL. Django necesita conocer de alguna forma la capa de la base de datos.py. ayuda para la productividad. e incluye más que sólo denición de columnas de la base de datos. pero introduce algunos problemas. ½estás leyendo un libro que fue escrito por autores y producido por un editor! revisión 789 del 9 de noviembre de 2009 .52 CAPÍTULO 7. Si el framework tuviera que introspeccionar la base de datos cada vez que se procese una petición. La ventaja de un tipo de datos de alto nivel es la alta productividad y la reusabilidad de código.

También tiene uno o más autores (una relación muchos-a-muchos con autores) y un único editor (una relación uno a muchos -.ForeignKey(Publisher) publication_date = models. éste es todo el código que necesitamos para tener acceso básico a los datos con Django. Django crea una tabla adicional -.con editores).Model. "website" varchar(200) NOT NULL ).CharField(maxlength=30) last_name = models. Un libro tiene un título y una fecha de publicación. El nombre de atributo corresponde al nombre de columna. o Sra.6. "city" varchar(60) NOT NULL. La primer cosa a notar es que cada modelo es representado por una clase Python que es una subclase de django.7. ingresa lo siguiente: from django. La clase antecesora.que maneja la correlación entre libros y autores. El primer paso para utilizar esta conguración de base de datos con Django es expresarla como código Python. dirección de correo electrónico y una foto tipo carnet.CharField(maxlength=50) city = models.Model): name = models. En nuestros modelos de Book tiene un la tabla de la base de datos ManyToManyField llamado authors. Esto signica que un libro tiene uno o más autores. "address" varchar(50) NOT NULL.db import models class Publisher(models. En efecto.models.ImageField(upload_to='/tmp') class Book(models. "state_province" varchar(30) NOT NULL.EmailField() headshot = models. .ManyToManyField(Author) publisher = models.: modelo Publisher CharField) corresponde al tipo de columna de la base de datos (ej. en una sintaxis compacta y agradable.CharField(maxlength=10) first_name = models.CharField(maxlength=60) state_province = models.también conocida como clave foránea -. Un editor tiene un nombre.CharField(maxlength=100) authors = models.Model): title = models. "name" varchar(30) NOT NULL. Lo creas o no.CharField(maxlength=30) address = models. una ciudad.CharField(maxlength=50) website = models. y el tipo de campo (ej.). apellido.URLField() class Author(models. TU PRIMER MODELO 53 Supondremos los siguientes conceptos. Por ejemplo.Model): salutation = models.db.CharField(maxlength=40) email = models. CREATE TABLE automáticamente como veremos en un momento. un estado o provincia. campos y relaciones: Un autor tiene un título (ej. Django puede generar esta sentencia ejemplo. pero Book no tiene una columna authors.una revisión 789 del 9 de noviembre de 2009 tabla de join muchos-a-muchos -. "country" varchar(50) NOT NULL.: es equivalente a la siguiente tabla (asumiendo la sintaxis de PostgreSQL para varchar). CREATE TABLE): el CREATE TABLE "books_publisher" ( "id" serial NOT NULL PRIMARY KEY.: Sr. En el archivo models. nombre. contiene toda la maquinaria necesaria para hacer que estos objetos sean capaces de interactuar con la base de datos -y que hace que nuestros modelos sólo sean responsables de denir sus campos.CharField(maxlength=30) country = models. una dirección. un país y un sitio Web. y cada atributo de un modelo generalmente corresponde a una columna en esa tabla.DateField() Examinemos rápidamente este código para conocer lo básico. En su lugar. La excepción a la regla una-clase-por-tabla es el caso de las relaciones muchos-a-muchos. Cada modelo generalmente corresponde a una tabla única de la base de datos. Model.py que se creó con el comando startapp.

los autores de este libro preeren poner una coma después de cada elemento de una tupla. ) Temporalmente.7. 'django. ejecuta capturar todos los problemas comunes del modelo.XViewMiddleware'. INSTALLED_APPS = ( #'django.contrib. Hacemos esto agregando la aplicación en el archivo de conguración.sessions'. Por omisión. Éstas dependen 'mysite. que lleva al paquete de la aplicación. La salida del error python manage. Tiende a debe brindarte información útil acerca de qué es lo que está mal en el código.py validate. Si todo está bien.auth'.contrib.auth. el primer paso es activar estos modelos en nuestro proyecto Django.py validate El comando mensaje validate verica si la sintaxis y la lógica de tus modelos son correctas.contrib. se ve como esto: INSTALLED_APPS = ( 'django.contrib. pero las activaremos y las discutiremos más adelante) Cuando termines. INSTALLED_APPS le indica a Django qué aplicaciones están activadas para un proyecto determinado. comenta estos cuatro strings poniendo un carácter (#) al principio.middleware.54 CAPÍTULO 7. 'django. ver el Apéndice B.. base de datos. De paso. y no hay penalización por el use de esa coma extra) 'mysite. Cada aplicación en INSTALLED_APPS Ahora que la aplicación Django ha sido activada en el archivo de conguración.sites'. (Están incluidos por omisión porque es frecuente usarlas. Cada vez que piensas que tienes problemas con tus modelos.py otra vez. debes notar que no hemos denido explícitamente una clave primaria en ninguno de estos modelos.SessionMiddleware'. Si no.contrib. Esto evita el problema de olvidar comas. Edita el archivo settings. ) (Como aquí estamos tratando con una tupla de un solo elemento. ) TEMPLATE_CONTEXT_PROCESSORS = () #.contrib. la ruta de paquetes.AuthenticationMiddleware'. Django dará automáticamente a cada modelo un campo de clave primaria entera llamado id. INTERACTUAR CON UNA BASE DE DATOS: MODELOS Para una lista completa de tipos de campo y opciones de sintaxis de modelos. revisión 789 del 9 de noviembre de 2009 . Entonces.common.contrib.contenttypes'. A no ser que le indiques lo contrario.books' se reere a la aplicación books en la que estamos trabajando.CommonMiddleware'.sessions. separados por puntos. #'django. de algunas de manera MIDDLEWARE_CLASSES = ( # 'django.sites'.contrib. # 'django. modica las conguraciones por omisión de MIDDLEWARE_CLASSES y de las aplicaciones que hemos comentado. #'django. # 'django. Instalar el modelo books a la lista de aplicaciones instaladas Ya escribimos el código.esto es. no olvides la coma nal. 'mysite.middleware. verás el 0 errors found. Para hacerlo. 'django. agrega que la conguración termine viéndose así: TEMPLATE_CONTEXT_PROCESSORS.contrib. creemos ahora las tablas en la base de datos. validemos los modelos ejecutando este comando: python manage.contrib. Finalmente. Es un requerimiento el que cada modelo Django tenga una clave primaria de columna simple. aunque la tupla tenga sólo un elemento. podemos crear las tablas en nuestra es representada por su ruta Python completa -. asegúrate de haber escrito el código del modelo correctamente.doc.auth'.middleware..sessions'. #'django. y examina la variable de conguración INSTALLED_APPS.middleware. # 'django.books'. Primero. 7.books' a la lista INSTALLED_APPS.contenttypes'.

INSTALAR EL MODELO 55 Si tus modelos son válidos. Puedes sobreescribir este compor- Como mencionamos antes. serial (PostgreSQL). La salida del ejemplo está en la sintaxis de PostgreSQL. CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY. Por convención. "title" varchar(100) NOT NULL. books es el nombre de la aplicación. como se detalla en el Apéndice B. o integer primary key (SQLite).7.py sqlall books En este comando. Cuando ejecutes el comando. "first_name" varchar(30) NOT NULL. también puedes sobreescribir esto. "headshot" varchar(100) NOT NULL ). COMMIT. Es lo que hayas especicado manage. Lo mismo sucede con el uso de las comillas simples o dobles en los nombres de columna.sólo imprime una salida en la pantalla para que puedas ver qué SQL ejecutaría Django si le pidieras que lo hiciera. También puedes sobreescribir esto. "author_id" integer NOT NULL REFERENCES "books_author" ("id"). book. "country" varchar(50) NOT NULL. Como ya puedes imaginar. CREATE TABLE "books_book" ( "id" serial NOT NULL PRIMARY KEY. debes ver algo como esto: cuando ejecutaste el comando BEGIN. "salutation" varchar(10) NOT NULL. "email" varchar(75) NOT NULL. por ti. "website" varchar(200) NOT NULL ). ejecuta el siguiente comando para que Django genere sentencias tus modelos en la aplicación books CREATE TABLE para (con sintaxis resaltada en colores disponible si estás usando Unix): python manage. Django agrega una clave primaria para cada tabla automáticamente -. Django agrega "_id" al nombre de campo de las claves foráneas.py startapp. auto_increment (MySQL). "name" varchar(30) NOT NULL. UNIQUE ("book_id". "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"). de manera que Django maneja automáticamente los tipos de campo especícos de cada base de datos. "last_name" varchar(40) NOT NULL. CREATE TABLE "books_publisher" ( "id" serial NOT NULL PRIMARY KEY. "publication_date" date NOT NULL ). Observa lo siguiente: Los nombres de tabla se generan automáticamente combinando el nombre de la aplicación (books) y el nombre en minúsculas del modelo (publisher. y author). puedes copiar y pegar este revisión 789 del 9 de noviembre de 2009 . "state_province" varchar(30) NOT NULL. CREATE INDEX books_book_publisher_id ON "books_book" ("publisher_id"). tamiento. "city" varchar(60) NOT NULL. "address" varchar(50) NOT NULL. CREATE TABLE "books_book_authors" ( "id" serial NOT NULL PRIMARY KEY. El comando sqlall no crea ni toca de ninguna forma tu base de datos -. "book_id" integer NOT NULL REFERENCES "books_book" ("id"). Si quieres.7. La relación de clave foránea se hace explícita con una sentencia Estas sentencias como REFERENCES CREATE TABLE son adaptadas a medida de la base de datos que estás usando. "author_id") ).los campos id.

A. Ejecuta el comando esta manera: syncdb de python manage. tengamos cuidado de una pequeña incomodidad. . llama el método Django ejecuta aquí una sentencia SQL INSERT. state_province='MA'. dependiendo de tu conguración psql para python manage. save() del objeto. revisión 789 del 9 de noviembre de 2009 . Para recuperar objetos de la base de datos. Detrás de la escena. Detrás Naturalmente.save() >>> p2 = Publisher(name="O'Reilly". 7. nada sucede... city='Cambridge'. Si estás interesado.: PostgreSQL) o puedes ejecutar el comando ejecutar.py dbshell. Django provee una manera más fácil de conrmar el envío del SQL a la base de datos. que deducirá qué cliente de linea de comando DATABASE_SERVER. Prueba ejecutando >>> from books. website='http://www.all() >>> publisher_list [<Publisher: Publisher object>.no hará desaparecer cosas.'.objects. <Publisher: Publisher object>] Estas pocas líneas logran bastantes resultados. country='U. state_province='MA'. sólo importa la clase del modelo apropiada y crea una instancia pasándole valores para cada campo. y las crea si no existen. siempre es seguro ejecutar python manage. Django provee automáticamente una API Python de alto nivel para trabajar con estos modelos. Estos son los puntos salientes: Para crear un objeto. toma un momento para bucear en el cliente de línea de comandos de tu servidor de bases de datos y ver las tablas que creó Django. country='U.8.'.objects.models import Publisher >>> p1 = Publisher(name='Addison-Wesley'. syncdb los modelos en cada aplicación que gure en tu variable de conguración para ver si las tablas apropiadas ya existen. address='10 Fawcett St.com/') >>> p2. city='Boston'.S. Publisher. De todas formas..py syncdb Verás algo como esto: Creating table books_publisher Creating table books_book Creating table books_author Installing index for books. porque no has agregado ningún modelo a la aplibooks ni has incorporado ninguna aplicación en INSTALLED_APPS. (Más sobre esto después.py syncdb -.) Si ejecutas cación El comando syncdb es una simple sincronización de tus modelos hacia tu base de datos. Ergo. Observa que no maneja esto. verica la base de datos syncdb no sincroniza los cambios o eliminaciones de los modelos.all(). Django ejecuta aquí una sentencia SQL SELECT.apress.py syncdb de nuevo.com/') >>> p1.py shell y escribiendo lo siguiente: Una vez que has creado un modelo. usa el atributo todos los objetos Publisher en la base de datos con la sentencia de escenas.S.oreilly.56 CAPÍTULO 7. . website='http://www.save() >>> publisher_list = Publisher. puedes hacer mucho con la API de base de datos de Django -. Para guardar el objeto en la base de datos..pero primero.A.. Acceso básico a datos python manage. El mismo examina todos python manage. Busca una lista de Publisher. INTERACTUAR CON UNA BASE DE DATOS: MODELOS fragmento de SQL en tu cliente de base de datos. ..objects. address='75 Arlington Street'. Puedes ejecutar manualmente el cliente de línea de comandos (ej. o usa los pipes de Unix para pasarlo directamente. si haces un cambio o modicas un modelo.'. y quieres actualizar la base de datos.Book model INSTALLED_APPS.. . Esto último es casi siempre más conveniente..

db import models class Publisher(models. un entero -.Model): title = models.ForeignKey(Publisher) publication_date = models. un método más fácil de entender: >>> from books. sal del shell Python y entra de nuevo con python manage.CharField(maxlength=50) city = models. Si __str__() no devuelve un string -si retorna. El único requerimiento para __str__() es que devuelva un string.DateField() def __str__(self): return self. Un método __str__() le dice a Python como mostrar la representación string de un objeto. todo lo que obtuvimos fue esta salida poco útil que hacía difícil distinguir los objetos [<Publisher: Publisher object>.9.CharField(maxlength=40) email = models.all() >>> publisher_list [<Publisher: Addison-Wesley>.CharField(maxlength=30) address = models.ManyToManyField(Author) publisher = models. self.junta los campos first_name y last_name.first_name. digamos.CharField(maxlength=10) first_name = models.objects.7. (Esta es la manera más simple de hacer que los cambios en el código tengan efecto.title sentación textual. Agregar strings de representación del modelo Publisher: Cuando imprimimos la lista de editores.9. Para que los cambios sean efectivos. AGREGAR STRINGS DE REPRESENTACIÓN DEL MODELO 57 7.models import Publisher >>> publisher_list = Publisher.ImageField(upload_to='/tmp') def __str__(self): return ' %s %s' % (self.CharField(maxlength=50) website = models.last_name) class Book(models.Model): salutation = models. <Publisher: O'Reilly>] Asegúrate de que cada modelo que denas tenga un método cuando necesita mostrar objetos.) Ahora la lista de objetos Publisher es Como puedes ver.Model): name = models.CharField(maxlength=60) state_province = models. Aquí.CharField(maxlength=30) country = models. sino también porque Django usa la salida de __str__() en muchos lugares . Puedes ver esto en acción agregando un método __str__() a los tres modelos: from django. <Publisher: Publisher object>] Podemos arreglar esto fácilmente agregando un método llamado __str__() a nuestro objeto Publisher.CharField(maxlength=100) authors = models. pero el __str__() del Author es un poco más complejo -. revisión 789 del 9 de noviembre de 2009 __str__() -.URLField() def __str__(self): return self.entonces Python generará un TypeError con un mensaje como "__str__ returned non-string".py shell.CharField(maxlength=30) last_name = models.no solo por tu propia conveniencia cuando usas el intérprete interactivo. los métodos __str__() puede hacer lo que sea que necesite hacer para devolver una repre__str__() de Publisher y Book devuelven simplemente el nombre y título del objeto respectivamente.name class Author(models.EmailField() headshot = models.

__str__() es un buen ejemplo de agregar comportamiento a los modelos. state_province = 'CA'.'. sin crear un nuevo registro (es decir.S.id 52 # esto será diferente según tus datos Las subsecuentes llamadas a ejecutarán una sentencia SQL save() guardarán el registro UPDATE en lugar de un INSERT): en su lugar. primero crea una instancia de tu modelo usando argumentos por nombre.S. website='http://www. también describe toda funcionalidad que el __str__() es un ejemplo de esa funcionalidad -. para realizar la sentencia SQL del objeto: save() INSERT).apress. website) VALUES ('Apress'.com/').A. state_province. Seleccionar objetos La creación y actualización de datos seguro es divertida. <Publisher: O'Reilly>.com/') Este acto de instanciar una clase modelo no toca la base de datos.11.save() La sentencia save() del párrafo anterior resulta aproximadamente en la sentencia SQL siguiente: UPDATE book_publisher SET name = 'Apress Publishing'.. 'CA'. address='2855 Telegraph Ave.apress. city='Berkeley'. . '2855 Telegraph Ave.58 CAPÍTULO 7.all() [<Publisher: Addison-Wesley>. city. . INTERACTUAR CON UNA BASE DE DATOS: MODELOS Finalmente. city = 'Berkeley'.com' WHERE id = 52. state_province='CA'.. 'Berkeley'. .un modelo sabe cómo mostrarse.10.. country='U. la llamada inicial a más: calcula el valor de la clave primaria para el registro y lo establece como el valor del atributo save() hace una cosa id de la instancia: >>> p. country = 'U. esto puede ser traducido directamente en lo siguiente: INSERT INTO book_publisher (name..objects.A. 'http://www.. Un modelo Django describe más que la conguración de la tabla de la base de datos. Insertar y actualizar datos Ya has visto cómo se hace: para insertar una la en tu base de datos. 'U. llama al método >>> p... pero también es inútil sin una forma de tamizar los datos.'. address = '2855 Telegraph Ave.'... 7. .. Como el modelo Publisher usa una clave primaria autoincremental id.'.'. . 7.'.apress. observa que objeto sepa hacer. country.S. <Publisher: Apress Publishing>] Eso se traslada a esto en SQL: revisión 789 del 9 de noviembre de 2009 . address. Ya hemos visto una forma de examinar todos los datos de un determinado modelo: >>> Publisher.save() En SQL. website = 'http://www.A. >>> p.name = 'Apress Publishing' >>> p. como: >>> p = Publisher(name='Apress'. Para guardar el registro en la base de datos (esto es.

Todos los modelos automáticamente obtienen un administrador vez que quieras consultar sobre una instancia del modelo.all(): Publisher.objects. Notar que por omisión la búsqueda usa el operador SQL tipos de búsquedas: = para realizar búsquedas exactas. usa el modelo para esto. city. name. website FROM book_publisher WHERE country = 'U. country.11. Este es un método del administrador objects que retorna todas las las de la base de datos.llamaremos métodos del administrador adjunto al modelo en el cual queremos hacer nuestra consulta. state_province. tenemos objects. Por ahora. debes usar el mismo cada all(). Para ello usaremos el método filter(): >>> Publisher. state_province="CA") [<Publisher: Apress Publishing>] Esos múltiples argumentos son traducidos a cláusulas SQL se traduce a lo siguiente: AND. country.7. todo lo que necesitas saber es que los administradores se encargan de todas las operaciones a nivel de tablas sobre los datos incluidos. Aunque este objeto se parece a una lista.un objeto que representa algún conjunto de las de la base de datos. tenemos objects. address. Técnicamente. Publisher.S. sólo trataremos estos como listas emuladas. Para más sobre el Zen de Python. la mayoría de las veces lo que vamos a necesitar es manejarnos sólo con un subconjunto de los datos. y (más importante) listar los campos sigue el principio del Zen de Python: Explícito es import this en el prompt de Python. intenta escribiendo Echemos un vistazo a cada parte de esta linea SELECT * puede ser lento. Puedes pasarle a filter() múltiples argumentos para reducir las cosas aún más: >>> Publisher. Esto es una decisión de diseño: en determinadas circunstancias mejor que implícito.A. Filtrar datos Aunque obtener todos los objetos es algo que ciertamente tiene su utilidad. website FROM book_publisher WHERE name = 'Apress Publishing'. Finalmente. y lo más importante.A. Por lo tanto el ejemplo en el fragmento de código SELECT id. Para el resto de este capítulo. Aquí no hay nada extraño: cuando En primer lugar. address. Los administradores son discutidos en el Apéndice B. El ejemplo anterior sería traducido en algo como: SELECT id. country. state_province. city.". state_province.1.filter(country="U. Cualquier búsqueda en base de datos va a seguir esta pauta general -. address.objects.filter(name="Apress Publishing") [<Publisher: Apress Publishing>] filter() toma argumentos de palabra clave que son traducidos en las cláusulas SQL WHERE apropiadas. El Apéndice C describe QuerySets en detalle. esto es un administrador (manager ). Nota Nota que Django no usa SELECT * cuando busca datos y en cambio lista todos los campos explícitamente. city.objects. tenemos nuestro modelo denido.11.' AND state_province = 'CA'. quieras buscar datos. Luego. las consultas. es realmente un QuerySet -.S. 7. Existen también otros revisión 789 del 9 de noviembre de 2009 . website FROM book_publisher. name. SELECCIONAR OBJETOS 59 SELECT id. name.

No estás imaginándote cosas. <Publisher: Apress Publishing>. un QuerySet).objects. country. website FROM book_publisher ORDER BY name.") Traceback (most recent call last): .objects..objects. y range (consultas SQL BETWEEN).60 CAPÍTULO 7. El Apéndice C describe en detalle todos 7.order_by("address") [<Publisher: O'Reilly>. city. 7.get(name="Penguin") Traceback (most recent call last): .. country. podrías descubrir que los objetos son devueltos en lo que parece ser un orden aleatorio. <Publisher: Addison-Wesley>] >>> Publisher. Ordenar datos A medida que juegas con los ejemplos anteriores. Django usa el doble guión bajo para indicar que algo mágico está sucediendo -. website FROM book_publisher WHERE name LIKE ' %press %'.objects. una consulta cuyo resultado sean múltiples objetos causará una excepción: >>> Publisher.11. No quisiéramos que una página Web que muestra una lista de editores estuviera ordenada aleatoriamente. Del mismo modo que Python.objects.S. city. Podemos ordenar por cualquier campo que deseemos: >>> Publisher. de manera que simplemente estamos recibiendo datos con algún orden arbitrario seleccionado por la base de datos. name. probablemente querremos usar nuestros datos en listas más útiles: order_by() para reordenar >>> Publisher. Para esto existe el método get(): >>> Publisher.get(name="Apress Publishing") <Publisher: Apress Publishing> En lugar de una lista (o más bien. icontains (LIKE no sensible a diferencias de mayúscustartswith y endswith. pero el SQL incluye ahora un ordenamiento especíco: SELECT id. Así que. <Publisher: Apress Publishing>. un poco ingenuo. Debido a eso.3.11. state_province. <Publisher: O'Reilly>] revisión 789 del 9 de noviembre de 2009 . obviamente. este método retorna un objeto individual. esos tipos de búsqueda.filter(name__contains="press") [<Publisher: Apress Publishing>] Notar el doble guión bajo entre SQL name y contains. <Publisher: O'Reilly>] Esto no se ve muy diferente del ejemplo de all() anterior.aquí la parte LIKE: __contains es traducida por Django en una sentencia SELECT id.it returned 2! Una consulta que no retorne objeto alguno también causará una excepción: >>> Publisher.order_by("state_province") [<Publisher: Apress Publishing>.order_by("name") [<Publisher: Addison-Wesley>. DoesNotExist: Publisher matching query does not exist. incluyendo las/minúsculas).. address.A. Obtener objetos individuales En ocasiones desearás obtener un único objeto. name.2.objects..get(country="U. INTERACTUAR CON UNA BASE DE DATOS: MODELOS >>> Publisher. hasta ahora no le hemos indicado a la base de datos cómo ordenar sus resultados. state_province. <Publisher: Addison-Wesley>. AssertionError: get() returned more than one Publisher -. Hay disponibles varios otos tipos de búsqueda.objects. address. en la práctica. Eso es.

7.11. SELECCIONAR OBJETOS

61

y por múltiples campos:

>>> Publisher.objects.order_by("state_provice", "address") [<Publisher: Apress Publishing>, <Publisher: O'Reilly>, <Publisher: Addison-Wesley>]
También podemos especicar un ordenamiento inverso antecediendo al nombre del campo un prejo menos):

-

(el símbolo

>>> Publisher.objects.order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>]
Aunque esta exibilidad es útil, usar

order_by()

todo el tiempo puede ser demasiado repetitivo. La mayor parte

del tiempo tendrás un campo particular por el que usualmente desearás ordenar. Es esos casos Django te permite anexar al modelo un ordenamiento por omisión para el mismo:

class Publisher(models.Model): name = models.CharField(maxlength=30) address = models.CharField(maxlength=50) city = models.CharField(maxlength=60) state_province = models.CharField(maxlength=30) country = models.CharField(maxlength=50) website = models.URLField() def __str__(self): return self.name class Meta: ordering = ["name"]
Este fragmento ante

order_by(),

ordering = ["name"]

le indica a Django que a menos que se proporcione un ordenamiento medi-

todos los editores deberán ser ordenados por su nombre.

¾Qué es este asunto de Meta?
Django usa esta

class Meta

interna como un lugar en el cual se pueden especicar metadatos

adicionales acerca de un modelo. Es completamente opcional, pero puede realizar algunas cosas muy útiles. Examina el Apéndice B para conocer las opciones que puede poner bajo

Meta.

7.11.4.

Encadenar búsquedas

Has visto cómo puedes ltrar datos y has visto cómo ordenarlos. En ocasiones, por supuesto, vas a desear realizar ambas cosas. En esos casos simplemente encadenas las búsquedas entre sí:

>>> Publisher.objects.filter(country="U.S.A.").order_by("-name") [<Publisher: O'Reilly>, <Publisher: Apress Publishing>, <Publisher: Addison-Wesley>]
Como podrías esperar, esto se traduce a una consulta SQL conteniendo tanto un

WHERE

como un

ORDER BY:

SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE country = 'U.S.A.' ORDER BY name DESC;
Puedes encadenar consultas en forma consecutiva tantas veces como desees. No existe un límite para esto.

7.11.5.

Rebanar datos

Otra necesidad común es buscar sólo un número jo de las. Imagina que tienes miles de editores en tu base de datos, pero quieres mostrar sólo el primero. Puedes hacer eso usando la sintaxis estándar de Python para el rebanado de listas:

>>> Publisher.objects.all()[0] <Publisher: Addison-Wesley>
revisión 789 del 9 de noviembre de 2009

62

CAPÍTULO 7. INTERACTUAR CON UNA BASE DE DATOS: MODELOS

Esto se traduce, someramente, a:

SELECT id, name, address, city, state_province, country, website FROM book_publisher ORDER BY name LIMIT 1;
Y más...
Hemos sólo arañado la supercie del manejo de modelos, pero deberías ya conocer lo suciente para entender todos los ejemplos del resto del libro. Cuando estés listo para aprender los detalles completos detrás de las búsquedas de objetos, échale una mirada al Apéndice C.

7.12.

Eliminar objetos
delete()
de tu objeto:

Para eliminar objetos, simplemente llama al método

>>> p = Publisher.objects.get(name="Addison-Wesley") >>> p.delete() >>> Publisher.objects.all() [<Publisher: Apress Publishing>, <Publisher: O'Reilly>]
Puedes también borrar objetos al por mayor llamando a

delete()

en el resultado de algunas búsquedas:

>>> publishers = Publisher.objects.all() >>> publishers.delete() >>> Publisher.objects.all() []
Nota
Los borrados son permanentes, así que, ½se cuidadoso!. En efecto, es usualmente una buena idea evitar eliminar objetos a menos que realmente tengas que hacerlo -- las base de datos relacionales no tiene una característica deshacer muy buena, y recuperar desde copias de respaldo es doloroso. A menudo es una buena idea agregar banderas activo a tus modelos de datos. Puedes buscar sólo objetos activos, y simplemente jar el campo activo a de la bandera.

False en lugar de eliminar el objeto.

Entonces, si descubres que has cometido un error, puedes simplemente volver a conmutar el estado

7.13.

Realizar cambios en el esquema de una base de datos
syncdb previamente en este capítulo, hicimos notar que syncdb simplemente crea

Cuando presentamos el comando

tablas que todavía no existen en tu base de datos -- no sincroniza cambios en modelos ni borra modelos. Si agregas o modicas un campo de un modelo o si eliminas un modelo, será necesario que realices el cambio en tu base de datos en forma manual. Esta sección explica cómo hacerlo. Cuando estás realizando cambios de esquema, es importante tener presente algunas características de la capa de base de datos de Django: Django se quejará estrepitosamente si un modelo contiene un campo que todavía no ha sido creado en la tabla de la base de datos. Esto causará un error la primera vez que uses la API de base de datos de Django para consultar la tabla en cuestión (esto es, ocurrirá en tiempo de ejecución y no en tiempo de compilación). A Django no le importa si una tabla de base de datos contiene columnas que no están denidas en el modelo. A Django no le importa si una base de datos contiene una tabla que no está representada por un modelo. El realizar cambios al esquema de una base de datos es cuestión de cambiar las distintas piezas -- el código Python y la base de datos en sí misma -- en el orden correcto. revisión 789 del 9 de noviembre de 2009

7.13. REALIZAR CAMBIOS EN EL ESQUEMA DE UNA BASE DE DATOS

63

7.13.1.

Agregar campos

Cuando se agrega un campo a una tabla/modelo en un entorno de producción, el truco es sacar ventaja del hecho que a Django no le importa si una tabla contiene columnas que no están denidas en el modelo. La estrategia es agregar la columna en la base de datos y luego actualizar el modelo Django para que incluya el nuevo campo. Sin embargo, tenemos aquí un pequeño problema del huevo y la gallina, porque para poder saber cómo debe expresarse la nueva columna en SQL, necesitas ver la salida producida por el comando

manage.py sqlall de Django,

el cual requiere que el campo exista en el modelo. (Notar que no es un requerimiento el que crees tu columna con exactamente el mismo SQL que usaría Django, pero es una buena idea el hacerlo para estar seguros de que todo está en sincronía). La solución al problema del huevo y la gallina es usar un entorno de desarrollo en lugar de realizar los cambios en un servidor de producción. (Estás usando un entorno de pruebas/desarrollo, ¾no es cierto?). Este es el detalle de los pasos a seguir. Primero, realiza los siguientes pasos en el entorno de desarrollo (o sea, no en el servidor de producción): 1. Agrega el campo a tu modelo. 2. Ejecuta

manage.py sqlall [yourapp]

para ver la nueva sentencia

CREATE TABLE

para el mod-

elo. Toma nota de la denición de la columna para el nuevo campo. 3. Arranca el shell interactivo de tu base de datos (por ej.

dbshell).

Ejecuta una sentencia

ALTER TABLE

psql

o

mysql,

o puedes usar

manage.py

que agregue tu nueva columna.

4. (Opcional) Arranca el shell interactivo de Python con (por ej.

manage.py shell

y verica que el nuevo

campo haya sido agregado correctamente importando el modelo y seleccionando desde la tabla

MyModel.objects.all()[:5]).

Entonces en el servidor de producción realiza los siguientes pasos: 1. Arranca el shell interactivo de tu base de datos. 2. Ejecuta la sentencia

ALTER TABLE

que usaste en el paso 3 de arriba.

3. Agrega el campo a tu modelo. Si estás usando un sistema de control de revisiones de código fuente y has realizado un check in de la modicación del paso 1 del trabajo en el entorno de desarrollo, entonces puedes actualizar el código (por ej. servidor de producción. 4. Reinicia el servidor Web para que los cambios en el código surtan efecto. Por ejemplo, hagamos un repaso de los que haríamos si agregáramos un campo

svn update

si usas Subversion) en el

num_pages al modelo Book descrito

previamente en este capítulo. Primero, alteraríamos el modelo en nuestro entorno de desarrollo para que se viera así:

class Book(models.Model): title = models.CharField(maxlength=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() num_pages = models.IntegerField(blank=True, null=True) def __str__(self): return self.title
(Nota: Revisa el apartado Agregando columnas NOT NULL para conocer detalles importantes acerca de porqué hemos incluido similar a esto:

blank=True

y

Luego ejecutaríamos el comando

null=True). manage.py sqlall books para ver la sentencia CREATE TABLE. La misma se vería

CREATE TABLE "books_book" ( "id" serial NOT NULL PRIMARY KEY, "title" varchar(100) NOT NULL, "publisher_id" integer NOT NULL REFERENCES "books_publisher" ("id"), "publication_date" date NOT NULL, "num_pages" integer NULL );
revisión 789 del 9 de noviembre de 2009

64

CAPÍTULO 7. INTERACTUAR CON UNA BASE DE DATOS: MODELOS

La nueva columna está representada de la siguiente manera:

"num_pages" integer NULL psql
A continuación, arrancaríamos el shell interactivo de base de datos en nuestra base de datos de desarrollo escribiendo (para PostgreSQL), y ejecutaríamos la siguiente sentencia:

ALTER TABLE books_book ADD COLUMN num_pages integer;
Agregando columnas NOT NULL
Existe un detalle sutil aquí que merece ser mencionado. Cuando agregamos el campo a nuestro modelo, incluimos las opciones crees. Sin embargo, es también posible agregar columnas que no puedan contener valores NULL. Para hacer esto, tienes que crear la columna como Por ejemplo: algunos valor(es) por omisión, y luego modicar la columna para activar el modicador

blank=True

y

null=True.

num_pages

Lo hicimos porque una

columna de una base de datos contendrá inicialmente valores NULL desde el momento que la

NULL, luego poblar los valores de la columna usando NOT NULL.

BEGIN; ALTER TABLE books_book ADD COLUMN num_pages integer; UPDATE books_book SET num_pages=0; ALTER TABLE books_book ALTER COLUMN num_pages SET NOT NULL; COMMIT;
Si sigues este camino, recuerda que debes quitar Luego de la sentencia

blank=True

y

null=True

de tu modelo.

ALTER TABLE,

vericaríamos que el cambio haya funcionado correctamente, para ello inicia-

ríamos el shell de Python y ejecutaríamos este código:

>>> from mysite.books.models import Book >>> Book.objects.all()[:5]
Si dicho código no produjera errores, podríamos movernos a nuestro servidor de producción y ejecutaríamos la sentencia

ALTER TABLE

en la base de datos de producción. Entonces, actualizaríamos el modelo en el entorno de

producción y reiniciaríamos el servidor Web.

7.13.2.

Eliminar campos

Eliminar un campo de un modelo es mucho más fácil que agregar uno. Para borrar un campo, sólo sigue los siguientes pasos: 1. Elimina el campo de tu modelo y reinicia el servidor Web. 2. Elimina la columna de tu base de datos, usando un comando como este:

ALTER TABLE books_book DROP COLUMN num_pages;
7.13.3. Eliminar campos Many-to-Many

Debido a que los campos many-to-many son diferentes a los campos normales, el proceso de borrado es diferente: 1. Elimina el campo

ManyToManyField

de tu modelo y reinicia el servidor Web.

2. Elimina la tabla many-to-many de tu base de datos, usando un comando como este:

DROP TABLE books_books_publishers;
7.13.4. Eliminar modelos

Eliminar completamente un modelo es tan fácil como el eliminar un campo. Para borrar un modelo, sigue los siguientes pasos: 1. Elimina el modelo de tu archivo

models.py

y reinicia el servidor Web.

2. Elimina la tabla de tu base de datos, usando un comando como este:

DROP TABLE books_book;
revisión 789 del 9 de noviembre de 2009

7.14. ¾QUÉ SIGUE?

65

7.14.

¾Qué sigue?

Una vez que has denido tus modelos, el paso siguiente es el poblar tu base de datos con datos. Podrías tener datos legados, en cuyo caso el Capítulo 16 te aconsejará acerca de cómo integrar bases de datos heredadas. Podrías delegar en los usuario del sitio la provisión de los datos, en cuyo caso el Capítulo 7 te enseñará cómo procesar datos enviados por los usuarios mediante formularios. Pero en algunos casos, tú o tu equipo podrían necesitar ingresar datos en forma manual, en cuyo caso sería de ayuda el disponer de una interfaz basada en Web para el ingreso y el manejo de los datos. El Duplicate explicit target name: próximo capítulo.

`próximo capítulo`_

está dedicado a la interfaz de administración de Django, la cual existe precisamente por esa razón.

revisión 789 del 9 de noviembre de 2009

66

CAPÍTULO 7. INTERACTUAR CON UNA BASE DE DATOS: MODELOS

revisión 789 del 9 de noviembre de 2009

Capítulo 8

El sitio de Administración Django
Para cierto tipo de Sitios Web, una interfaz de administración es una parte esencial de la infraestructura. Se trata de una interfaz basada en web, limitada a los administradores autorizados, que permite agregar, editar y eliminar el contenido del sitio. La interfaz que usas para escribir en tu blog, el sitio privado que los editores usan para moderar los comentarios de los lectores, la herramienta que tus clientes utilizan para actualizar los comunicados de prensa en la web que construiste para ellos -- todos son ejemplos de interfaces de administración. Aunque hay un problema con las interfaces de administración: es aburrido construirlas. El desarrollo web es divertido cuando estás desarrollando funcionalidades de lado público del sitio, pero construir interfaces de administración es siempre lo mismo. Tienes que autenticar usuarios, mostrar y manipular formularios, validar las entradas y demás. Es aburrido y repetitivo. ¾Cuál es la solución de Django para estas tareas aburridas y repetitivas? Las hace todas por ti -- en sólo un par de líneas de código, ni más ni menos. Con Django, construir interfaces de administración es un problema resuelto. Este capítulo trata sobre la interfaz de administración automática de Django. Esta característica funciona leyendo los meta-datos en tus modelos para brindar una interfaz potente y lista para producción que los administradores del sitio podrán usar inmediatamente. Aquí discutimos cómo activar, usar y personalizar esta utilidad.

8.1.

Activar la interfaz de administración

Pensamos que la interfaz de administración es la característica más atractiva de Django -- y la mayoría de Djangonautas están de acuerdo -- pero como no todo el mundo lo necesita, es una pieza opcional. Esto signica que hay que dar tres pasos para activar la interfaz de administración: 1. Agrega meta-datos de administración a tus modelos. No todos los modelos pueden (o deberían) ser editables por los usuarios administradores, por lo que necesitas marcar los modelos que deberían tener una interfaz de administración. Esto lo hacemos añadiendo al modelo una clase interna anterior, usamos:

Admin

(junto con la clase

una). Así que, para agregar una interfaz de administración a nuestro modelo

Meta, si es Book del

que hay capítulo

class Book(models.Model): title = models.CharField(maxlength=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() num_pages = models.IntegerField(blank=True, null=True) def __str__(self): return self.title class Admin: pass declaración de Admin

La

marca la clase como poseedora de una interfaz de administración. Hay

una serie de opciones que podemos incluir bajo comportamiento por defecto, así que escribimos está vacía.

Admin, pero por pass para decirle

ahora vamos a limitarnos al a Python que la clase

Admin

revisión 789 del 9 de noviembre de 2009

como se muestra en la Figura 6-3. En caso contrario no serás capaz de identicarte para entrar a la interfaz urls..defaults import * urlpatterns = patterns(''. etc. ) Eso es todo. Agrega el patrón de URL en tu syncdb con "django. de tu archivo de conguración admin. te preguntara algo sobre crear un superusuario.urls. y como tal debería ser lo sucientemente clara como para explicarse por sí misma.contrib.sessions".py syncdb. De cualquier forma.97. Cada campo denido en tu modelo aparece aquí.auth".admin. identicarte. 5. Django version 0.contrib.py.68 CAPÍTULO 8.) revisión 789 del 9 de noviembre de 2009 . include('django. Instalar la aplicación Admin a las clases Publisher y Author. y jugar un poco.0. using settings 'mysite. Cada objeto al que se le dió una declaración la Figura 6-2 Admin aparece en el índice de la página principal. (r'^admin/'. Además. Más adelante hablaremos sobre esto. Esto se hace agregando "django. "django.0. probablemente sea buena idea agregar ahora declaraciones de 2. (Por ejemplo.2. Ahora ejecuta esto: python manage. 0 errors found. como se muestra en listas de cambio 6 y Los enlaces para agregar y modicar objetos llevan a dos páginas a las que nos referiremos como formularios de edición 7 de objetos: Las listas de cambio son esencialmente páginas de índices de objetos en el sistema.contrib.0. Si aún estás usando el que fue creado por startproject. verás que puedes gestionar usuarios. Usarás el nombre de usuario y la clave que conguraste cuando agregaste tu superusuario.py.. ya que la aplicación 3.contrib.contrib.auth" en IN- STALLED_APPS. los campos de fecha/hora tienen controles tipo calendario.1:8000/ Quit the server with CONTROL-C. Lo primero que verás es una página de identicación. Si no lo hiciste en ese momen- django/contrib/auth/bin/create_superuser. 4. el patrón de la URL de administración ya debería estar ahí. Los formularios de edición se usan para modicar objetos existente y crear nuevos (mira la Figura 6-4). Usar la interfaz de administración La interfaz de administración está diseñada para ser usada por usuarios no técnicos. como se muestra en la Figura 6-1.admin" a tus INSTALLED_APPS settings. Este paso instalará las tablas de la base de datos que la interfaz de administración necesita.contrib. Ahora puedes visitar la URL que te brinda Django (http://127.1:8000/admin/ en el ejemplo precedente).py para crear este usuario administrador. Ejecuta python manage. las claves foráneas usan cajas de selección. EL SITIO DE ADMINISTRACIÓN DJANGO Si estás siguiendo este ejemplo escribiendo tu propio código. no están comentadas. los patrones de URL deberían terminar siendo algo así: from django. Aún así. Hay varias opciones que pueden controlar los campos que aparecen en esas listas y la aparición de características extra como campos de búsqueda e accesos directo a ltros predenidos. Nota Es probable que la primera vez que ejecutaste to.0. Una vez identicado.urls')). grupos y permisos (veremos más sobre esto en breve). También descomenta todas las líneas de MIDDLEWARE_CLASSES congurando la tupla. y borra la denición de TEMPLATE_CONTEXT_PROCESSOR para permitir que tome los valores por "django. asegurate de que las aplicaciones y "django.contenttypes" defecto. tendrás que ejecutar de administración. pero comentado.conf. y notarás que campos de tipos diferentes tienen diferentes controles. admin depende de ellas. se brindan unas pocas notas sobre sus características. Verás algo como Validating models. 8.settings' Development server is running at http://127.py runserver para iniciar el servidor de pruebas.

USAR LA INTERFAZ DE ADMINISTRACIÓN 69 Figura 8.2. revisión 789 del 9 de noviembre de 2009 .8.1: Pantalla de autenticación de Django.

revisión 789 del 9 de noviembre de 2009 .2: El índice principal de la Administración de Django. EL SITIO DE ADMINISTRACIÓN DJANGO Figura 8.70 CAPÍTULO 8.

8.3: Una típica vista de lista de cambio revisión 789 del 9 de noviembre de 2009 . USAR LA INTERFAZ DE ADMINISTRACIÓN 71 Figura 8.2.

72 CAPÍTULO 8. EL SITIO DE ADMINISTRACIÓN DJANGO Figura 8.4: Un típico formulario de edición revisión 789 del 9 de noviembre de 2009 .

Si está desactivada.Model): title = models. Cambia la declaración de Admin como sigue: class Book(models. 8. indica que el usuario es considerado un miembro del sta en tu organización). Si le das a alguien el permiso de editar usuarios.1. se volvería tan difícil como encontrar una aguja en un pajar. seguidos de un conjunto de campos que denen lo que el usuario tiene permitido hacer en la interfaz de administración. asignando Nota El acceso a editar usuarios y permisos también es controlado por el sistema de permisos. Sin embargo. y verás esos avisos de esos errores cuando intentes guardar el objeto. Grupos y Permisos Desde que estás identicado como un superusuario. Los vínculos a los modelos Los objetos el resto de los modelos que haz denido. sitios restringidos no administrativos. PERSONALIZAR LA INTERFAZ DE ADMINISTRACIÓN 73 Te darás cuenta que la interfaz de administración también controla por ti la validez de los datos ingresados. Primero. Sin embargo. esta opción diferencia entre usuarios públicos y administradores. no superusuarios y miembros del sta -. Mira el Capítulo 12.). Cada cambio realizado a través de la interfaz de administración es registrado. funciones de búsqueda y ltros y a esta interfaz. Usuarios y Grupos se encuentran en el índice de la página principal junto con todo usuario tienen el los campos estándar nombre de usuario. Personalizar la interfaz de administración Libro.tienen accesos que dependen del conjunto de permisos concedidos. la lista de cambio de nuestros libros sólo muestra la cadena de representación del modelo que agregamos con el método __str__ Esto funciona bien sólo para algunos libros. Un grupo es simplemente un conjunto de permisos a aplicar a todos los usuarios de ese grupo. 8. activos. como se muestra en la Figura 6-5.3. Como el mismo sistema de usuarios puede usarse para controlar el acceso al sitio público (es decir. Cada objeto editable a través de la interfaz de administración tiene tres permisos: un permiso de crear permisos a un usuario habilitas que este acceda a realizar la acción que el permiso describe. tienes acceso a crear. En esta sección sólo vamos a cubrir algunas de ellas relacionadas con nuestro modelo la personalización de la interfaz de administración en detalle. verás el botón Historia en la esquina superior derecha de la ventana. el usuario no tendrá acceso a ninguna URL que requiera identicación. la interfaz de administración solicita una conrmación para prevenir costosos errores. fácilmente podremos agregar algunas columnas.8.3. hay un conjunto de tres opciones seleccionables: La opción Es sta  indica que el usuario está habilitado a ingresar a la interfaz de administración (por ejemplo. La opción es superusuario da al usuario completo e irrestricto acceso a todos los elementos de la interfaz de administración. y puedes examinar este registro haciendo click en este botón (mira la Figura 6-6). 8 . como si fuese cualquier otro objeto. ½estará en condiciones de editar sus propios permisos. La opción Activo dene si el usuario está activo en todo sentido. Puedes editar estos usuarios y permisos a través de la interfaz de administración. Los grupos son útiles para otorgar idénticos permisos a un gran número de usuarios. Lógicamente. un permiso de modicar 9 . que probablemente no es lo que querías! También puedes asignar usuarios a grupos. La eliminación de un objeto se desencadena en cascada.2.esto es. y sus permisos regulares son ignorados. Los administradores normales -. Como estamos ahora. contraseña. Usuarios. y la página de conrmación de eliminación del objeto muestra todos los objetos relacionados que se eliminarán con él (mira la Figura 6-7). y nombre real que puedes esperar. la interfaz de administración tiene un sistema de permisos de usuario que puedes usar para darle a otros usuarios acceso limitado a las partes de la interfaz que ellos necesitan. Cuando editas un objeto existente. y un permiso de eliminar 10 . Cuando eliminas un objeto existente. dirección de correo. El `Capítulo 17`_ descubre Puedes personalizar el aspecto y la forma en que la interfaz de administración se comporta de varias maneras. Intenta dejar un campo requerido en blanco o poner una fecha inválida en un campo de fecha.CharField(maxlength=100) revisión 789 del 9 de noviembre de 2009 . editar y eliminar cualquier objeto. pero si tuviéramos cientos o miles de libros.

74 CAPÍTULO 8.5: Un formulario de edición mostrando errores revisión 789 del 9 de noviembre de 2009 . EL SITIO DE ADMINISTRACIÓN DJANGO Figura 8.

PERSONALIZAR LA INTERFAZ DE ADMINISTRACIÓN 75 Figura 8. revisión 789 del 9 de noviembre de 2009 .8.3.6: Página de historia de un objeto django.

76 CAPÍTULO 8.7: Una página de conrmación de eliminación de un objeto Django revisión 789 del 9 de noviembre de 2009 . EL SITIO DE ADMINISTRACIÓN DJANGO Figura 8.

con sólo algunas líneas de código. si dentro del subdirectorio base_site. sus interfaces usan el sistema de plantillas propio de Django.html. sólo edita el nuevo archivo admin/base_site. la opción buscará el texto en el campo con Django en el título). Mira el capítulo 10 para obtener revisión 789 del 9 de noviembre de 2009 más información sobre cómo funciona esto. Luego. search_fields crea un campo que permite buscar texto.html. No te olvides del subdirectorio admin. o donde sea que Django fue instalado. Personalizar la apariencia de la interfaz de administración Claramente.html: copia esta desde el directorio original a tu directorio personalizado y haz los TEMPLATE_DIRS estaba vació al principio. . El sitio de administración de Django está propulsado por el mismo Django. booleanos.ForeignKey(Publisher) publication_date = models. Por defecto.) Como explicamos en el Capítulo 4.) search_fields = ('title'.) Estas cuatro líneas de código cambian dramáticamente la interfaz de nuestra lista. Es simplemente una lista de campos con los cuales ordenar el resultado. el editor y la fecha de publicación. 'publication_date') list_filter = ('publisher'. simplemente copia el conjunto relevante de plantillas de la distribución Django en uno de los directorios apuntados por plantilla El sitio de administración muestra Administración de Django en la cabecera porque esto es lo que se incluye en la admin/base_site. La opción editor. Por ejemplo. Finalmente. Estaremos habilitados a ltrar por fecha (que te permite ver sólo los libros publicados la última semana. mes. ordenamos por fecha de publicación con los más recientes al principio. Puede que te preguntes cómo. En este ejemplo. copia la original dentro de un subdirectorio llamado admin dentro de cualquiera del directorio de TEMPLATE_DIRS que estés usando. Nota que cualquier plantilla por defecto de Django Admin puede ser reescrita. En nuestro caso. 8.DateField() class Admin: list_display = ('title'. pero las claves foráneas.4. cuando se cargan plantillas Django. haz lo mismo que hicimos con cambios sobre esta copia. como se muestra en la gura 6-8. (El sistema de plantillas de Django fue presentado en el Capítulo 4. entonces copia django/contrib/admin/templates/admin/base_site a /home/misplantillas/admin/base_site. y campos con un atributo de opciones crea una barra de ltrado del lado derecho de la lista. La opción ordering controla el orden en el que los objetos son presentados en la interfaz de ad- ministración. nombre de tu propio sitio. Por defecto. tal como lo quieres ver.4. 'publication_date') ordering = ('-publication_date'. Cada una de estas líneas indica a la interfaz de administración que construya diferentes piezas de la interfaz: La opción list_display controla que columnas aparecen en la tabla de la lista. Para personalizar las plantillas del sitio de administración.html. si tu TEMPLATE_DIRS incluye "/home/misplantillas". anteponiendo un signo menos a un campo se obtiene el orden reverso.ManyToManyField(Author) publisher = models. usando el sistema de plantillas de Django. Django encuentra las plantillas por defecto de la interfaz de administración. Para personalizar esta plantilla base_site. esta plantilla se encuentra en el directorio de plantillas de administración django/contrib/admin/templates. tener la frase Administración de Django en la cabecera de cada página de administración es ridículo. Aquí podemos cambiar eso para mostrar el título. Los ltros aparecen cuando tienen al menos 2 valores de dónde elegir.) y por choices son las que mejor funcionan. por defecto. la conguración de TEMPLATE_DIRS especica una lista de directorios a vericar TEMPLATE_DIRS. la lista de list_filter cambios muestra una sola columna que contiene la representación en cadena de caracteres del objeto.8. Puedes indicarle a la interfaz de administración que ltre por cualquier campo. Para reescribir una plantilla. Django automáticamente busca plantillas templates/ de cada paquete de aplicación como alternativa. por el de Django. etc. fechas. Es sólo un texto de relleno que es fácil de cambiar.html para reemplazar el texto genérico de Django. PERSONALIZAR LA APARIENCIA DE LA INTERFAZ DE ADMINISTRACIÓN 77 authors = models. hacer una interfaz de edición de datos realmente potente y lista para producción. 'publisher'. La respuesta es que. título (entonces podrías ingresar Django para mostrar todos los libros Usando estas opciones (y las otras descriptas en el capítulo 17) puedes. que puedes encontrar buscando en tu directorio site-packages de Python.

EL SITIO DE ADMINISTRACIÓN DJANGO Figura 8.78 CAPÍTULO 8.8: Página de lista de cambios modicada revisión 789 del 9 de noviembre de 2009 .

De hecho. una de sus características sobresalientes. diríamos que es una de sus killer feautures. Obviamente. Para detalles completos sobre la personalización del sitio de administración de Django. aquí se muestran todas las aplicaciones. Ejecuta el comando bastante útil. cuando surgen 8. revisión 789 del 9 de noviembre de 2009 . después de todo. No obstante.6. cambiar el orden para hacer más fácil ubicar determinada aplicación que estás buscando. Es un punto de partida 8. En el ¾Qué sigue? `próximo capítulo`_. En otras palabras. Mientras el periodista ingresa datos a Django.html. A lo largo de los años.5. y verás que usa una etiqueta llamada as app_list %}. (Recuerda copiar admin/index. de acuerdo a la conguración que tenga INSTALLED_APPS. La interfaz de administración de Django brilla especialmente cuando usuarios no técnicos necesitan ser capaces de ingresar datos. El desarrollador diseña un modelo basado en esta información y luego abre la interfaz de administración para el periodista. Gestión de datos adquiridos : Hay una pequeña entrada de datos asociada a un sitio como problemas con los datos automáticos. la razón de ser de la interfaz de administración de Django es facilitar el trabajo simultáneo de productores de contenido y programadores. la página inicial es probablemente la más importante de la interfaz de administración. tener una una interfaz gráca al modelo revela problemas rápidamente. Django ofrece otro acceso directo en este apartado. Si código explícito en una plantilla no te satisface. y debería ser fácil utilizarla. por ejemplo.5.un reporte especial sobre la calidad del agua del acueducto municipal. puedes ver el etiquetas de plantillas. el desarrollo de una característica típica online -. es útil poder entrar y editarlos fácilmente. Edita el archivo.or puesto que la mayoría de los datos provienen de una fuente automática. Después de todo. el programador puede enfocarse en desarrollar la interfaz accesible públicamente (½la parte divertida!). La plantilla para personalizarla es tillas propio como en el ejemplo previo). más allá de estas tareas de entrada de datos obvias. http://chicagocrime.implicaba algo así: El periodista responsable del artículo se reúne con uno de los desarrolladores y discuten sobre la información disponible. Esto es usual para encontrar errores de modelado. puedes tener la intención de personalizar la apariencia (el look & feel ) de la página principal del administrador. mira el admin/index. Sospechamos que la gran mayoría de lectores de este libro tiene una horda de tareas de este tipo.html a tu directorio de plan{ % get_admin_app_list `Capítulo 10`_ para encontrar detalles sobre cómo implementar tu propias python manage. hemos descubierto algunos patrones donde pensamos que usar la interfaz de administración resulta útil. formularios. Sin embargo. a menudo nos preguntan sobre casos de uso para la interfaz de administración (¾Cuándo debemos usarlo y por qué?). PERSONALIZAR LA PÁGINA ÍNDICE DEL ADMINISTRADOR 79 8. encontramos que la interfaz de administración es útil en algunos otros casos: Inspeccionar modelos de datos : La primer cosa que hacemos cuando hemos denido un nuevo modelo es llamarlo desde la interfaz de administración e ingresar algunos datos de relleno. o sea. Quizás quieras. Personalizar la página índice del administrador En una nota similar.7. puedes incluir vínculos explícitos a objetos especícos de la manera que creas más conveniente. es muy útil para modicar datos (se veía venir). ese es el propósito detrás de esta característica. Sin embargo. Cuándo y porqué usar la interfaz de administración Pensamos que la interfaz de administración de Django es bastante espectacular. nos meteremos en el verdadero guiso del desarrollo Web: creación y procesamiento de Hasta ahora hemos creado algunos modelos y congurado una interfaz de primera clase para modicar datos.py adminindex <aplicación> `Capítulo 17`_. En el periódico donde Django fue creado originalmente.8. para obtener un pedazo de código de plantilla para incluir en la página índice del administrador. Por defecto. Esta etiqueta devuelve todas las aplicaciones Django instaladas. pongamos -. En vez de usar esta etiqueta. el administrador es difícil de superar. ordenados por el nombre de la aplicación. Si tenemos cualquier tipo de tarea de introducción de datos.

6 N.: Can delete del T. 8 N. 10 N. 9 N. 7 N.: En el control de selección de permisos aparece como change list es el nombre que recibe en inglés edit forms es el nombre que recibe en inglés Can add revisión 789 del 9 de noviembre de 2009 .: del T.: Can change del T.80 CAPÍTULO 8. EL SITIO DE ADMINISTRACIÓN DJANGO Duplicate explicit target name: próximo capítulo. del T.: del T.

'') if query: qset = ( Q(title__icontains=query) | Q(authors__first_name__icontains=query) | Q(authors__last_name__icontains=query) ) results = Book.views): (patrones).urls). Y a partir de ahí. A menudo.POST. ese request. viendo cómo manejar los datos suministrados al navegador.search') al conjunto de URL patterns search en nuestro módulo de vistas (mysite. from django. la diferencia entre el éxito y el fracaso de un sitio. Comenzaremos haciendo un simple formulario de búsqueda a mano. que se cubren en el apéndice H. { "results": results. Recuerda que esto se hace agregando algo como A continuación. ¾no? Comenzaremos agregando la vista para la búsqueda a nuestro URLconf (mysite. lo determina la calidad de su búsqueda.books. aunque un poco simple.books.Capítulo 9 Procesamiento de formularios Autor invitado: Simon Willison Si has estado siguiendo el capítulo anterior.GET.distinct() else: results = [] return render_to_response("books/search. Los datos del POST se acceden de manera similar. ya deberías tener un sitio completamente funcional. Así es cómo accedes a los datos del GET desde Django. pasaremos al uso del framework de formularios que trae Django. Búsquedas En la Web todo se trata de búsquedas.1.views. Así que sería mejor que agreguemos un poco de búsqueda a nuestro pequeño sitio de libros.filter(qset).objects.models import Q from django.html". Google y Yahoo.shortcuts import render_to_response from models import Book def search(request): query = request. a través de un objeto llamado request. revisión 789 del 9 de noviembre de 2009 .get('q'.db. han construido sus empresas multimillonarias alrededor de las búsquedas. Casi todos los sitios observan un gran porcentaje de tráco viniendo desde y hacia sus páginas de búsqueda. Dos de los casos de éxito más grandes. escribiremos la vista (r'^search/$'. Estos objetos se comportan exactamente como los diccionarios estándar de Python.GET. 'mysite. En este capítulo trataremos con la próxima pieza del juego: cómo construir vistas que obtienen entradas desde los usuarios. 9. y tienen además otras capacidades. "query": query }) Aquí han surgido algunas cosas que todavía no vimos. La primera.

y que internamente usa el operador LIKE de SQL en la base de datos. Este método get() es el get('q'. Q? Los objetos Q se utilizan para ir construyendo consultas complejas -. se habría lanzado un Segundo. Técnicamente.w3. y puede leer más sobre esto en el apéndice C. estos objetos de un QuerySet. Esto lo solucionará: .html query = request.01//EN"> <html lang="en"> <head> <title>Search{ % if query %} Results{ % endif %}</title> </head> <body> <h1>Search</h1> <form action="." method="GET"> <label for="q">Search: </label> <input type="text" name="q" value="{{ query|escape }}"> <input type="submit" value="Search"> </form> { % if query %} <h2>Results for "{{ query|escape }}":</h2> { % if results %} <ul> { % for book in results %} <li>{{ book|escape }}</l1> { % endfor %} </ul> { % else %} <p>No books found</p> { % endif %} { % endif %} </body> </html> revisión 789 del 9 de noviembre de 2009 .82 CAPÍTULO 9. que es mismo que posee cualquier diccionario de Python. PROCESAMIENTO DE FORMULARIOS ¾Qué son estos datos del GET y del POST? GET y POST son los dos métodos que emplean los navegadores para enviar datos a un servidor. y los nombres de ambos concuerdan con la consulta). '') busca un parámetro del GET llamado Observa que estamos usando el método que q y retorna una cadena de texto vacía si este parámetro no fue suministrado.en este caso. Dado que estamos buscando en campos de muchos-a-muchos. En estas consultas.get('q'. Todavía no hay una plantilla para esta vista. Hay diferencias de semántica importantes entre el GET y el POST. ¾qué es ese buscando los libros que coincidan en el título o en el nombre con la consulta. Así que la línea: /books/search/ si quieres http://www. así que usamos (el string vacío). estamos Q consisten es una búsqueda en la que no se distinguen mayúsculas de minúsculas (case- KeyError. request. Si hubiéramos intentado acceder a la variable simplemente usando hubiese estado disponible en los datos del GET. Los encontrarás con frecuencia en los elementos form de HTML: <form action="/books/search/" method="get"> Esto le indica al navegador que suministre los datos del formulario a la URL empleando el método GET. Al agregar en el ltrado.org/2001/tag/doc/whenToUseGet.distinct() <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4. '') para proporcionar un valor por omisión. y q no icontains insensitive ). que no vamos a ver ahora mismo. algo potencialmente confuso. Lo estamos usando aquí para ser precavidos: no es seguro asumir '' request.GET. se eliminan los resultados duplicados. pero diríjete a aprender más.GET tiene una clave 'q'.GET. es posible que un libro se obtenga más de una vez (por ej: un libro que tiene dos autores. get() de request.GET['q'].

en la plantilla base).books. lo pasamos por el ltro cualquier búsqueda potencialmente maliciosa sea descartada antes de que se inserte en la página ½Es vital hacer esto con todo el contenido suministrado por el usuario! De otra forma el sitio se abre a ataques de cross-site scripting (XSS). <label> de HTML. 'mysite. en el formulario. Cuando tu sitio tiene millones de usuarios. lo insertaremos en un nuevo archivo forms. 9. hay unas pocas sutilezas que vale la pena resaltar: action es . Se podría mejorar más el sitio colocando el formulario de búsqueda en cada página (esto es. La accesibilidad y la usabilidad importan aquí. Comenzaremos agregando (r'^contact/$'. Muchos sitios parecen olvidar esto. Los campos deberían rellenarse con los datos previamente suministrados.9.podemos pasar directamente la consulta a la base de datos. obviamente. Se le proporciona una descripción de los campos del formulario. Creación de un formulario para comentarios La mejor forma de construir un sitio que la gente ame es atendiendo a sus comentarios. el formulario debería volver a mostrarse. empleando una clase de Python. Ahora ya tenemos la búsqueda funcionando. cuando intentas formarte una audiencia.3.py dentro del directorio de nuestra aplicación: revisión 789 del 9 de noviembre de 2009 . ocultan los detalles de su contacto en FAQs. lo que esto hace debería ser obvio. El formulario debería volver a mostrarse una y otra vez. el framework de formularios de Django está diseñado para hacer la mayor parte del trabajo por ti. EL FORMULARIO PERFECTO 83 A esta altura. y parecen dicultar lo más posible el encuentro con las personas.contact') al URLconf. El `Capítulo 19`_ discute XSS y la seguridad con más detalle. para evitarle al usuario tener que volver a tipear todo nuevamente. hasta que todos los campos se hayan rellenado correctamente. Esta es una buena práctica estándar: no utilices vistas distintas para la página que contiene el formulario y para la página con los resultados. Consideremos el comportamiento de un hipotético formulario perfecto: Debería pedirle al usuario cierta información. He aquí la clase para nuestro simple formulario.2. reglas de validación. Escribamos entonces un simple formulario para comentarios. Por convención. En cambio. Esto es posible gracias a que la capa de base de datos de Django se encarga de manejar este aspecto de la seguridad por ti. Así que es importante el uso inteligente del elemento proporcionar ayuda contextual útil. Así que la validación es esencial. Los datos suministrados deberían ser sometidos a una validación extensiva. junto a mensajes de error detallados e informativos. En todo lugar que aparece query y book. esto puede ser una estrategia razonable. El formulario perfecto Los formularios pueden ser a menudo una causa importante de frustración para los usuarios de tu sitio.views. A continuación veremos un ejemplo más complejo. Si el usuario ha cometido algún error. y luego denamos nuestro formulario. no necesitamos preocuparnos por el contenido malicioso en las búsquedas de la base de datos -. Volvemos a insertar el texto de la consulta en el <input>. esto signica la URL actual. y usémoslo para ilustrar al framework de Django en plena acción. Los formularios en Django se crean de una manera similar a los modelos: declarativamente. usa una página única para las dos cosas. La regla de oro para la seguridad de una aplicación web es nunca confíes en la información que ingresa. y una simple plantilla. deberías pedir comentarios cada vez que se presente la oportunidad. Esto permite a los usuarios renar fácilmente escape para asegurarnos de que sus búsquedas sin tener que volver a teclear todo nuevamente. y Django hace el resto. En cambio. ½Construir el formulario perfecto pareciera llevar mucho trabajo! Por suerte. Pero antes de hacerlo. Sin embargo. El resultado es un formulario perfecto que requiere de muy poco esfuerzo. discutamos un tópico más abstracto: el formulario perfecto.2. Dejaremos esto de tarea para el hogar. y también lo es 9.

shortcuts import render_to_response models import Book forms import ContactForm def search(request): query = request.objects.96/newforms/. Y si estás en perezoso. que es un campo de caracteres. "query": query }) revisión 789 del 9 de noviembre de 2009 . Sin embargo. ('bug'. '') if query: qset = ( Q(title__icontains=query) | Q(authors__first_name__icontains=query) | Q(authors__last_name__icontains=query) ) results = Book. tal como un modelo de Django es una django. puede construir un conjunto de mensajes de error útiles. en http://www.com/documentation/0. 'General enquiry'). En views. Incluyamos esto en una vista y veámoslo en acción. puede generar sus propios widgets de HTML. Al momento de escribir ese libro.py: from from from from django. que se nuevos tipos si ninguno cubre tus necesidades. y puedes escribir El objeto formulario sabe cómo hacer una cantidad de cosas útiles por sí mismo. El módulo django.Model. y referencia a django.djangoproject. fue rescrito y ahora se llama newforms (nuevos formularios). ('suggestion'. y el nuevo paquete como django. que es un campo de correo electrónico y es opcional (porque incluso los comentarios anónimos pueden ser útiles).forms hará referencia al nuevo paquete de formularios. En algún momento esto va django. un mensaje.forms.db. Puede validar una colección de datos.models. para los campos. Una lista completa de éstas últimas se encuentra disponible en la documentación de Django. { "results": results. Hay una cantidad de otros tipos de campos disponibles. estar seguros de que los ejemplos de este libro funcionen lo más ampliamente posible.Form. Como hacía muy dicultosa la producción de formularios. para django.html".newforms.get('q'. todos harán Un formulario de Django es una subclase de subclase de django. 'Bug report'). ) class ContactForm(forms.84 CAPÍTULO 9. y un emisor.distinct() else: results = [] return render_to_response("books/search. puede incluso dibujar el formulario completo por ti.Form): topic = forms. Nuestro ContactForm consiste de tres campos: un tópico.newforms.GET.newforms también contiene cierta cantidad de clases Field puede elegir entre tres opciones.EmailField(required=False) ¾New Forms? ¾Qué? Cuando Django fue lanzado al público por primera vez.db. PROCESAMIENTO DE FORMULARIOS from django import newforms as forms TOPIC_CHOICES = ( ('general'.models import Q django. 'Suggestion').filter(qset). Django actualmente viene con ambos paquetes.newforms. Sin embargo. como todavía hay cierta cantidad de código que depende del viejo sistema de formularios. poseía un sistema de formularios complicado y confuso.CharField() sender = forms.ChoiceField(choices=TOPIC_CHOICES) message = forms. el viejo sistema de formularios de Django sigue disponible como a cambiar.

Textarea()) sender = forms. Por el momento. así que la cambiaremos por un widget <textarea>: class ContactForm(forms.CharField(widget=forms.as_table }} </table> <p><input type="submit" value="Submit"></p> </form> </body> </html> al render_to_response. {'form': form}) y en contact.EmailField(required=False) El framework de formularios divide la lógica de presentación para cada campo. pero puedes sobreescribirlo fácilmente. y proveen a los formularios de accesibilidad desde fábrica.01//EN"> <html lang="en"> <head> <title>Contact us</title> </head> <body> <h1>Contact us</h1> <form action=". as_table <tr> La línea más interesante aquí es {{ form.html: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.Form): topic = forms. no sucede nada. o proporcionar uno nuevo de tu creación. en un conjunto de widgets. que fue pasada es un método de ese objeto que reproduce el formulario como una secuencia de renglones de una tabla (también pueden usarse as_ul y as_p). si se suministra el formulario. Agreguemos nuestras reglas de validación: revisión 789 del 9 de noviembre de 2009 ." method="POST"> <table> {{ form. Cada tipo de campo tiene un widget por defecto. debes denirlas por tu cuenta en la plantilla.as_table }}. CREACIÓN DE UN FORMULARIO PARA COMENTARIOS 85 def contact(request): form = ContactForm() return render_to_response('contact.9.ChoiceField(choices=TOPIC_CHOICES) message = forms. form es nuestra instancia de ContactForm. El HTML generado se ve así: <th><label for="id_topic">Topic:</label></th> <td> <select name="topic" id="id_topic"> <option value="general">General enquiry</option> <option value="bug">Bug report</option> <option value="suggestion">Suggestion</option> </select> </td> </tr> <tr> <th><label for="id_message">Message:</label></th> <td><input type="text" name="message" id="id_message" /></td> </tr> <tr> <th><label for="id_sender">Sender:</label></th> <td><input type="text" name="sender" id="id_sender" /></td> </tr> Observa que las etiquetas <table> y <form> no se han incluido. Nuestro formulario actualmente utiliza un widget queremos restringir a nuestros usuarios a una sola línea de texto. Los elementos label sí se incluyen.3.html'. Pero no te da control sobre el comportamiento del formulario al ser suministrado. Esto <input type="text"> para el campo del mensaje.

usaremos el paquete de correo electrónico de Django.com'}) Si nuestro formulario siempre usará los mismos valores por defecto.html'. PROCESAMIENTO DE FORMULARIOS def contact(request): if request. El framework de formularios hace más que validar los datos.86 CAPÍTULO 9.Textarea(). Podemos hacerlo empleando la función: revisión 789 del 9 de noviembre de 2009 . llamamos al método is_valid(): form = ContactForm(request.POST. La página se volverá a cargar. deseamos construir un correo electrónico que contenga los comentarios del usuario. usamos form. el framework de formularios se encarga de que se devuelvan como un valor entero de Python. initial="Replace with your feedback") 9. Podríamos sacarlos directamente del request. necesitamos saber si los datos son en verdad válidos. y se indica que la validación debe ser efectuada. A menudo. Podemos hacerlo con el argumento de palabras claves initial: form = CommentForm(initial={'sender': 'user@example. necesitamos una forma de accederlos.com') # . Observa que dado que sender no es obligatorio. respectivamente.clean_data. Finalmente. El direcciones. La manera más fácil de hacerlo es enviando un correo electrónico al administrador del sitio.POST) else: form = ContactForm() return render_to_response('contact. también los convierte a tipos de datos de Python.get('sender'. Procesamiento de los datos suministrados Una vez que el usuario ha llenado el formulario al punto de que pasa nuestras reglas de validación. mostrando un error de validación que informa que nuestro campo de mensaje es obligatorio. en un formulario editar.is_valid(): topic = form. Una instancia bound se construye con un diccionario (o un objeto que funcione como un diccionario) y sabe cómo validar y volver a representar sus datos. podemos congurarlos en la denición misma del formulario: message = forms.is_valid(): # Process form data Ahora necesitamos acceder a los datos. En cambio. {'form': form}) Una instancia de formulario puede estar en uno de dos estados: bound (vinculado) o unbound (no vinculado). 'noreply@example.clean_data['topic'] message = form. estos se vinculan. proveemos un valor por defecto por si no fue proporcionado. Nuestro formulario para comentarios sólo trata con texto. por lo menos a un nivel razonable.4. EmailField sabe cómo validar estas Cómo especicar datos iniciales Al pasar datos directamente al constructor del formulario. pero si estamos usando campos como IntegerField o DateTimeField.CharField(widget=forms.method == 'POST': form = ContactForm(request. necesitamos registrar los comentarios del usuario.por ejemplo. En este caso. y si lo son.clean_data: if form. Pero antes.POST) if form. Intenta hacer clic en Submit en el formulario vacío. necesitamos hacer algo útil con los datos. no nos estaríamos beneciando de la conversión de tipos que realiza el framework de formularios.. pero si lo hiciéramos. necesitamos mostrar un formulario inicial con algunos campos previamente rellenados -.clean_data['message'] sender = form. Un formulario unbound no tiene datos asociados y simplemente sabe cómo representarse a sí mismo. Para esto. Intenta también ingresar una dirección de correo electrónico inválida. o como un objeto datetime.. y enviarlo. Para saber si un formulario está vinculado (bound ) a datos válidos.

Nuestras propias reglas de validación Imagina que hemos lanzado al público a nuestro formulario de comentarios. Redirigir luego del POST es un patrón útil que puede ayudar a prevenir este escenario. y los correos electrónicos han empezado a llegar. La función de la vista nalizada se ve así: from from from from django.Form): revisión 789 del 9 de noviembre de 2009 .shortcuts import render_to_response django. redirige al usuario a otra página en lugar de retornar HTML directamente.5. Esta clase provee características avanzadas como adjuntos. sender. message.POST) if form. Si vamos a usar nuestra regla una y otra vez. la dirección del emisor.core. sobre los encabezados del mensaje.com'] ) La función send_mail tiene cuatro argumentos obligatorios: el asunto y el cuerpo del mensaje. necesitamos validación adicional sobre el campo a nuestro formulario: clean_message message. mensajes multiparte.. Esto probablemente lleve a un comportamiento no deseado. así que debemos agregar un método class ContactForm(forms. redirigiremos a nuestro usuario a una página estática de conrmación. podemos crear un nuevo tipo de campo. 9.html'. 'noreply@example.com') send_mail( 'Feedback from your site. Sin embargo. y un control completo Una vez enviado el mensaje con los comentarios. ['administrator@example. la mayoría de las validaciones que agreguemos serán de un solo uso.5. y pueden agregarse directamente a la clase del formulario.9. sender. la consulta se repetirá. Así que luego de que se haya procesado el POST con éxito. por ejemplo. topic: %s' % topic. topic: %s' % topic.get('sender'.http import HttpResponseRedirect django. NUESTRAS PROPIAS REGLAS DE VALIDACIÓN 87 from django.core. ['administrator@example. es poco probable que tengan algo interesante. send_mail es un código conveniente que envuelve a la clase EmailMessage de Django.method == 'POST': form = ContactForm(request.com'] ) return HttpResponseRedirect('/contact/thanks/') else: form = ContactForm() return render_to_response('contact.mail import send_mail # . En este caso. Nos encontramos con un problema: algunos mensajes vienen con sólo una o dos palabras. send_mail( 'Feedback from your site.mail import send_mail forms import ContactForm def contact(request): if request. que el registro se agregue dos veces a la base de datos. {'form': form}) Redirigir luego del POST Si un usuario selecciona actualizar sobre una página que muestra una consulta POST.clean_data['topic'] message = form. Hay varias formas de insertar nuestras propias validaciones en un formulario de Django.clean_data['message'] sender = form. message. Decidimos adoptar una nueva póliza de validación: cuatro palabras o más.is_valid(): topic = form..clean_data. y una lista de direcciones destino. por favor.

Textarea()) sender = forms. display: block. casi siempre desde la plantilla misma. El texto que lleva esta excepción se mostrará al usuario como un un elemento de la lista de errores. Si nos olvidamos de retornarlo. Podemos usar estas variables para construir nuestra propia plantilla para el formulario: <form action=". el validador de None y el valor original será perdido.errors }}. Usamos una combinación de len() y split() para contar la cantidad de palabras. necesitamos obtenerlos desde el diccionario clean_data del formulario.fieldname. Si el usuario ha ingresado muy pocas palabras. font-size: 10px. la lista de errores puede dotarse de mejoras visuales.errors }} <label for="id_topic">Kind of feedback:</label> {{ form. } .message. PROCESAMIENTO DE FORMULARIOS topic = forms. {{ form.ValidationError("Not enough words!") return message CharField obligatorio).EmailField(required=False) def clean_message(self): message = self.CharField(widget=forms.clean_data.88 CAPÍTULO 9. Es importante que retornemos explícitamente el valor del campo al nal del método.errors }} <label for="id_message">Your message:</label> revisión 789 del 9 de noviembre de 2009 . Cada widget de un campo (<input vidualmente accediendo a type="text">.as_table }} y similares son atajos útiles que podemos usar mientras desarrollamos nuestra aplicación. o similares) puede generarse indi{{ form. Esto nos permite modicar el valor (o convertirlo a otro tipo de Python) dentro de nuestro método de validación. '') num_words = len(message. padding: 4px 5px.fieldname }}. pero todo lo que concierne a la forma en que nuestro formulario es representado puede ser sobreescrito. En particular." method="POST"> <div class="fieldWrapper"> {{ form.get('message'.split()) if num_words < 4: raise forms. 9. Cualquier error asociado con un campo está disponible como {{ form.topic. Una presentación personalizada <ul> tiene asignada la clase La forma más rápida de personalizar la presentación de un formulario es mediante CSS.6. <select>. } </style> Si bien es conveniente que el HTML del formulario sea generado por nosotros. Dado que los datos del campo ya han sido procesados parcialmente. en muchos casos la disposición por defecto no quedaría bien en nuestra aplicación.errorlist { margin: 0. <textarea>.topic }} </div> <div class="fieldWrapper"> {{ form. El CSS a continuación hace que nuestros errores salten a la vista: <style type="text/css"> ul.errorlist li { background-color: red. y el elemento errorlist para ese propósito. margin: 0 0 3px.ChoiceField(choices=TOPIC_CHOICES) message = forms. padding: 0. se retornará Este nuevo método será llamado después del validador que tiene el campo por defecto (en este caso. color: white. lanzamos un error ValidationError.

message. una ciudad. y de autoridad. Nuestro modelo de la clase Publisher dice que un publicista tiene un nombre.errors }} la variable se muestra como un <ul class="errorlist"> si se presentan errores y como una cadena de caracteres en blanco si el campo es válido ( o si el formulario no está vinculado).message }} </div> En caso de que hubieran errores de validación. tal como la clase ContactForm que creamos manualmente con anterioridad.sender }} </div> <p><input type="submit" value="Submit"></p> </form> {{ form. También podemos tratar a form.POST) if form. un domicilio. un país. por ejemplo: <div class="fieldWrapper{ % if form. <div> contenedor y se muestran los 9.errors como a un booleano o incluso iterar sobre la misma como en una lista. Andy Hunt y Dave Thomas la denen como sigue.method == 'POST': form = PublisherForm(request.errors %} errors{ % endif %}"> { % if form. dentro de un sistema.message.message. es: no te repitas (del inglés Don't Repeat Yourself. se agrega la clase errors al errores en una lista ordenada. Una regla de oro que es importante en el desarrollo de software.save() return HttpResponseRedirect('/add_publisher/thanks/') else: form = PublisherForm() return render_to_response('books/add_publisher. y un sitio web. CREANDO FORMULARIOS A PARTIR DE MODELOS 89 {{ form. abreviado DRY). {'form': form}) revisión 789 del 9 de noviembre de 2009 .message }} </div> <div class="fieldWrapper"> {{ form.html'.9.sender.newforms import form_for_model PublisherForm = form_for_model(Publisher) PublisherForm es una subclase de Form. un estado o form_for_model(): provincia.errors %} <ol> { % for error in form. no ambigua.message. Creando formularios a partir de Modelos Construyamos algo un poquito más interesante: un formulario que suministre los datos de un nuevo publicista a nuestra aplicación de libros del Capítulo 5. podemos usar este útil atajo: from models import Publisher from django. a la que Django intenta adherirse.7. Podemos usarla de la misma forma: from forms import PublisherForm def add_publisher(request): if request.is_valid(): form. En cambio. en The Pragmatic Programmer : Cada pieza de conocimiento debe tener una representación única. estaríamos quebrando la regla anterior.message.errors }} <label for="id_sender">Your email (optional):</label> {{ form.7.errors %} <li><strong>{{ error|escape }}</strong></li> { % endfor %} </ol> { % endif %} {{ form. Si duplicamos esta información en la denición del formulario.

Dado que los formularios derivados de modelos se emplean a menudo para guardar nuevas instancias del modelo en la base de datos. volviendo para darle una mirada más de cerca a las vistas y a los URLconfs (introducidos por primera vez en el Capítulo 3). 9. form_for_model incluye un Este método trata con el uso común.html es casi idéntico a nuestra plantilla contact.8. Comenzaremos el Capítulo 8 yendo hacia atrás. revisión 789 del 9 de noviembre de 2009 . Re(r'^add_publisher/$'. así que la omitimos.add_publisher').html original. form_for_instance() es un método que está relacionado con el anterior. 'mysite. Los próximos trece capítulos tratan con varios tópicos avanzados. la clase del formulario creada por conveniente método tenga que ver con los datos suministrados. Este capítulo concluye con el material introductorio de este libro. PROCESAMIENTO DE FORMULARIOS El archivo cuerda además agregar un nuevo patrón al URLconf: add_publisher. pero puedes ignorarlo si deseas hacer algo más que Ahí se muestra un atajo más.books. incluyendo la generación de contenido que no es HTML (Capítulo 11). Esto es útil al crear formularios editar.views. seguridad (`Capítulo y entrega del servicio (`Capítulo 20`_). Luego de estos primeros siete capítulos. ¾Qué sigue? 19`_). deberías saber lo suciente como para comenzar a escribir tus propios proyectos en Django.90 CAPÍTULO 9. y puede crear formularios preinicializados a partir de la instancia de un modelo. save(). El resto del material de este libro te ayudará a completar las piezas faltantes a medida que las vayas necesitando.

2})hours/$'. hours_ahead).Capítulo 10 Vistas avanzadas y URLconfs En el Capítulo 3.defaults import * from mysite import views urlpatterns = patterns(''. que se pasa directamente como un método. (r'^now/in_chicago/$'. ) Django ofrece otra forma de especicar la función vista para un patrón en particular en la URLconf: se le puede pasar un string que contiene el nombre del módulo y de la función en lugar del método. views. views. (r'^now/plus(\d{1. (Por cada nueva función vista. now_in_london urlpatterns = patterns(''. hours_behind.2})hours/$'.1. (r'^now/in_london/$'. current_datetime). Continuando con el ejemplo: revisión 789 del 9 de noviembre de 2009 . views. 10.conf. son sólo código Python --. tienes que recordar importarla y la declaración de importaciones tiende a volverse demasiado larga si se utiliza este método). now_in_london).defaults import * from mysite. 10. (r'^now/plus(\d{1. Pero a medida que las aplicaciones Django crecen en complejidad.2})hours/$'. Este capítulo entra en detalle sobre funcionalidad avanzada en esas dos partes del framework. sus URLconf crecen también. cada entrada de la URLconf incluye su función vista asociada. Esto signica que es necesario importar las funciones view en la parte superior del módulo.conf. hours_ahead.1.urls. Importación de funciones de forma efectiva Considera esta URLconf. explicamos las bases de las funciones vista de Django y las URLconfs. Este ejemplo de URLconf es equivalente al anterior: from django.hours_ahead). hours_behind).hours_behind). now_in_chicago. Trucos de URLconf No hay nada de especial con las URLconfs -. que se basa en el ejemplo del Capítulo 3: from django. now_in_chicago). como se describe las secciones que siguen. (r'^now/in_london/$'. Es posible evitar esa tarea tediosa importando el módulo views directamente. views. ) Como se explicó en el Capítulo 3.como cualquier otra cosa en Django. (r'^now/minus(\d{1. (r'^now/$'.urls.now_in_chicago).now_in_london).views import current_datetime. (r'^now/minus(\d{1.1.2})hours/$'. Puedes aprovecharte de esto de varias maneras. views. (r'^now/in_chicago/$'.current_datetime). (r'^now/$'. y mantener esos import puede ser tedioso de manejar.

Django importa automáticamente la función vista apropiada la primera vez que sea necesaria. (r'^now/in_london/$'.92 CAPÍTULO 10. si usas la técnica del string.urls. Ambos enfoques son válidos e incluso puedes mezclarlos dentro de la misma URLconf.current_datetime. 'now_in_london'). así: lo cual es redundante. Usar múltiples prejos de vista En la práctica. probablemente termines mezclando vistas hasta el punto en que las vistas de tu URLconf no tengan un prejo común.views'.2})hours/$'.hours_behind').views.archive_index').views. Es más Pythónico -.views. (r'^now/minus(\d{1. (r'^now/in_chicago/$'.now_in_london'). 'now_in_chicago'). (r'^now/in_london/$'. Ver la sección Empaquetado de funciones vista más adelante en este capítulo. 'current_datetime').2})hours/$'. (r'^tag/(\w+)/$'. 'weblog. como la de pasar funciones como objetos.views. Podemos factorizar ese from django. 'mysite.conf.tag'). Otro atajo que puedes tomar al usar la técnica del string es sacar factor común de prejos view. porque no requiere que importes las funciones vista.defaults import * urlpatterns = patterns(''. Las siguientes son ventajas del enfoque del método: Permite un fácil empaquetado de funciones vista.1.urls. Simplemente junta los objetos Antes: patterns(). así: from django. (r'^now/minus(\d{1. 'hours_behind'). Estamos usando con comillas -.current_datetime'). Las siguientes son ventajas del enfoque string: Es más compacto. 'hours_ahead'). En nuestro ejemplo URLconf.views. todavía puedes sacar provecho del atajo del prejo de las vistas para remover esta duplicación.views. (r'^now/$'. 'mysite. ) (Nota que los nombres de las vistas están entre comillas. 'mysite. La elección es tuya. (r'^now/$'.views'. cada uno de los strings vista comienza con prejo común y pasarlo como primer argumento de 'mysite. ¾cuál es mejor? Realmente depende de tu estilo personal de programación y tus necesidades. (r'^now/plus(\d{1.defaults import * urlpatterns = patterns(''.now_in_chicago').conf.hours_ahead').2})hours/$'. ) Nota que no se pone un punto detrás del prejo. (r'^now/plus(\d{1.conf. ni un punto delante de los string vista. ) revisión 789 del 9 de noviembre de 2009 . Con estos dos enfoques en mente.urls. según el string que describe el nombre y la ruta de la función vista. Resulta en URLconfs más fáciles de leer y de manejar si tus funciones vista están extendidas por varios módulos Python diferentes. 10.defaults import * urlpatterns = patterns('mysite.en lugar de mysite. VISTAS AVANZADAS Y URLCONFS from django. (r'^/?$'. 'mysite.es decir. patterns().2.current_datetime' -- Al usar esta técnica ya no es necesario importar las funciones vista.archive_month'). (r'^(\d{4})/([a-z]{3})/$'. está más en línea con las tradiciones Python. Sin embargo. Django los pone automáticamente. (r'^now/in_chicago/$'.) 'mysite.views. 'mysite.views.2})hours/$'. 'mysite. 'mysite.views.views.

revisión 789 del 9 de noviembre de 2009 . ) if settings.conf import settings urlpatterns = patterns(''.archive_month'). Para hacer eso simplemente comprueba el valor de la conguración from django.1.3. grupos de expresiones regulares sin nombre -.defaults import* from django.conf.DEBUG: urlpatterns += patterns(''. (r'^tag/(\w+)/$'. como lo hacemos en este ejemplo. es posible usar grupos de expresiones regulares con nombre para capturar partes de la URL y pasarlos como argumentos clave a una vista.views. quizás quieras aprovechar esta técnica para alterar el comDEBUG en tiempo de ejecución.1. 'mysite. 'mysite. (r'^/?$'. urlpatterns.urls.conf.es decir. 10.homepage'). 'tag').10. (r'^debuginfo/$'. ) En este ejemplo. 'mysite. 'archive_index'). (r'^(\d{4})/([a-z]{3})/$'. Esta variable 10. Usar grupos con nombre Hasta ahora en todos nuestros ejemplos URLconf hemos usado. así: portamiento de tu URLconf mientras estás en el modo depuración de Django.urls.defaults import * urlpatterns = patterns('mysite. la URL /debuginfo/ sólo estará disponible si tu conguración DEBUG tiene el valor True. 'archive_month').views. ) Lo único que le importa al framework es que existe una variable a nivel módulo llamada puede ser construida de forma dinámica. (r'^$'.4.views. Casos especiales de URLs en modo Debug Hablando de construir urlpatterns de forma dinámica.debug'). ) urlpatterns += patterns('weblog.views'. (r'^(\d{4})/([a-z]{3})/$'. En un uso más avanzado.1. ponemos paréntesis en las partes de la URL que queremos capturar y Django le pasa ese texto capturado a la función vista como un argumento posicional.views'. TRUCOS DE URLCONF 93 Después: from django.

price='$2. quantity): print "Selling %s unit(s) of %s at %s" % (quantity.50'.50'. siempre y cuando los argumentos posicionales estén listados antes que los argumentos por palabra clave. quantity=6. quantity=6) sell('Socks'. se especican los nombres de los argumentos junto con los valores que se le pasan. 6) Para llamarla con argumentos de palabra clave. price='$2.50') sell(quantity=6. (r'^articles/(?P<year>\d{4})/(?P<month>\d{2})/$'. nombre es el nombre del grupo y patrón es algún patrón a buscar. Las siguientes sentencias son equivalentes a los ejemplos anteriores: sell('Socks'. views. quantity=6) sell('Socks'.50'. se especican los nombres de los argumentos junto con sus valores. item='Socks') Finalmente.year_archive). views. '$2.50') sell(price='$2. views. quantity=6) sell(price='$2. ) Aquí está la misma URLconf. VISTAS AVANZADAS Y URLCONFS Argumentos claves vs. considera esta sencilla función: def sell(item.urls. item='Socks'. quantity=6. price='$2. price='$2. (r'^articles/(\d{4})/$'.year_archive).conf.50'. ) Esto produce exactamente el mismo resultado que el ejemplo anterior. Las siguientes sentencias son equivalentes: sell(item='Socks'.month_archive). Argumentos posicionales A una función de Python se la puede llamar usando argumentos de palabra clave o argumentos posicionales -. price) Para llamarla con argumentos posicionales.50') En las expresiones regulares de Python.month_archive). en algunos casos.conf. la sintaxis para los grupos de expresiones regulares con nombre es donde (?P<nombre>patrón).50'. sencillamente pasas los argumentos sin especicar explícitamente qué argumento concuerda con cual valor.defaults import * from mysite import views urlpatterns = patterns(''. con una sutil diferencia: se le pasa a las funciones vista los valores capturados como argumentos clave en lugar de argumentos posicionales. item='Socks') sell(quantity=6. quantity=6) sell(item='Socks'. (r'^articles/(\d{4})/(\d{2})/$'. '$2. los dos al mismo tiempo.50'. price='$2. price. item='Socks'. Por ejemplo. con los grupos sin nombre una petición a equivalente a esto: revisión 789 del 9 de noviembre de 2009 /articles/2006/03/ resultaría en una llamada de función . Por ejemplo. price='$2. reescrita para usar grupos con nombre: from django.50'. (r'^articles/(?P<year>\d{4})/$'.y. quantity=6. Aquí hay un ejemplo de URLconf que usa grupos sin nombre: from django. views. En una llamada por argumento posicional. la asociación está implícita en el orden de los argumentos. se especican los argumentos en el orden en que están listados en la denición de la función: sell('Socks'. En una llamada por argumentos de palabra clave.94 CAPÍTULO 10.urls.defaults import * from mysite import views urlpatterns = patterns(''. se pueden mezclar los argumentos posicionales y por palabra clave. item.

) # views. Aún así.html'. la misma petición resultaría en esta llamada de función: month_archive(request. como mostramos a continuación: revisión 789 del 9 de noviembre de 2009 . (r'^bar/$'. otra ventaja de los grupos con nombres es la facilidad de lectura. pasará todos los argumentos sin nombre como argumentos posicionales.objects. especialmente para las personas que no están íntimamente relacionadas con las expresiones regulares o con tu aplicación Django en particular. usará esos.defaults import * from mysite import views urlpatterns = patterns(''. Django no generará ningún mensaje de error.5.filter(is_new=True) return render_to_response('template1. Por ejemplo. En ambos casos. Además. views. cambiar el orden de los parámetros capturados en la URL no tendría ningún 10. ignorando los argumentos sin nombre.py from django. Comprender el algoritmo de combinación/agrupación Una advertencia al usar grupos con nombre en una URLconf es que un simple patrón URLconf no puede contener grupos con nombre y sin nombre.objects. TRUCOS DE URLCONF 95 month_archive(request. Pasarle opciones extra a las funciones vista A veces te encontrarás escribiendo funciones vista que son bastante similares. 10. poniendo paréntesis alrededor de la URL para capturarla y comprobando la URL dentro de la vista para determinar la plantilla. {'m_list': m_list}) def bar_view(request): m_list = MyModel. usar grupos con nombres hace que tus URLconfs sean un poco más explícitas y menos propensas a bugs causados por argumentos -. a primera vista. algunos desarrolladores opinan que la sintaxis de los grupos con nombre es fea y larga. con tan sólo algunas pequeñas diferencias. Si haces eso.1.urls. y estuviéramos usando grupos sin nombre. con los grupos con nombre. digamos que tienes dos vistas cuyo contenido es idéntico excepto por la plantilla que utilizan: # urls. month='03') En la práctica. podrías pensar en reducir la redundancia usando la misma vista para ambas URLs. grupos sin nombre en una expresión regular: Si existe algún argumento con nombre. con respecto a grupos con nombre vs. en una URLconf que usa grupos con nombre.html'.1. si quisiéramos cambiar las URLs para incluir el mes antes del año. Aquí está especícamente el algoritmo que sigue el parser URLconf. '03') Sin embargo. pasará cualquier opción extra como argumentos de palabra clave. pero probablemente descubras que tus URLs no se están disparando de la forma esperada. {'m_list': m_list}) Con este código nos estamos repitiendo y eso no es elegante. (r'^foo/$'. Por supuesto.filter(is_new=True) return render_to_response('template2.bar_view).foo_view). Ver la próxima sección para más información.6.shortcuts import render_to_response from mysite. Al comienzo. '2006'.models import MyModel def foo_view(request): m_list = MyModel.y puedes reordenar los argumentos en las deniciones de tus funciones vista. Siguiendo con el ejemplo anterior. tendríamos que acordarnos de cambiar el orden de los argumentos en la vista efecto sobre la vista. views. los benecios de los grupos con nombre tienen el costo de la falta de brevedad. year='2006'.1. Si estuviéramos usando grupos con nombre.10. Es más fácil ver lo que está pasando.conf.py from django. month_archive.

Cada patrón en una URLconf puede incluir un tercer ítem: un diccionario de argumentos de palabra clave para pasarle a la función vista. el problema con esa solución es que acopla fuertemente tus URLs y tu código Si decides renombrar a /fooey/.urls.foobar_view). Esta técnica de la opción extra en la URLconf es una linda forma de enviar información adicional a tus funciones vista sin tanta complicación. template_name): m_list = MyModel.objects. La siguiente sección contiene algunas ideas sobre cómo puedes usar la técnica de la opción extra en la URLconf como parte de tus proyectos. tienes que recordar cambiar el código de la vista. {'m_list': m_list}) /foo/ Sin embargo.html' elif url == 'bar': template_name = 'template2. {'template_name': 'template1. Por ese motivo es que es usada por algunas aplicaciones incluidas en Django. más notablemente el sistema de vistas genéricas.urls.html' return render_to_response(template_name.py from django. En este caso puedes simular la captura de valores de la URL usando opciones extra de URLconf para manejar esa URL extra con una única vista. template_name en la URLconf.shortcuts import render_to_response from mysite.conf.objects. (r'^(bar)/$'.py from django.models import MyModel def foobar_view(request.html'}).shortcuts import render_to_response from mysite. (r'^bar/$'. {'template_name': 'template2. (r'^(foo)/$'.models import MyModel def foobar_view(request. revisión 789 del 9 de noviembre de 2009 . VISTAS AVANZADAS Y URLCONFS # urls.defaults import * from mysite import views urlpatterns = patterns(''. views. url): m_list = MyModel. views. views. {'m_list': m_list}) Como puedes ver.filter(is_new=True) return render_to_response(template_name.filter(is_new=True) if url == 'foo': template_name = 'template1. la URLconf en este ejemplo especica como a cualquier otro parámetro. que tratamos en el Capítulo 9. La solución elegante involucra un parámetro URLconf opcional. La función vista lo trata Simulando valores capturados en URLconf Supongamos que posees un conjunto de vistas que son disparadas vía un patrón y otra URL que no lo es pero cuya lógica de vista es la misma.conf. views.defaults import * from mysite import views urlpatterns = patterns(''.foobar_view). ) # views.foobar_view.py from django. Con esto en mente podemos reescribir nuestro ejemplo anterior así: # urls.html'}).96 CAPÍTULO 10.foobar_view. ) # views.py from django. (r'^foo/$'.

py from django. podrías tener una aplicación que muestra algunos datos para un día particular. con estas dos funciones Python: def say_hello(person_name): print 'Hello. (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$'.my_view.my_view). En lugar de pensar Esta vista muestra una lista de objetos Usemos este código como ejemplo: Event y Esta otra vista muestra una lista de objetos BlogEntry... que sería equivalente a /mydata/jan/06/.puedes capturar los mismos en una URLconf como esta (usando sintaxis de grupos con nombre): urlpatterns = patterns(''..defaults import * from mysite import views urlpatterns = patterns(''. puedes comenzar a hacer abstracciones de nivel más alto de tus vistas. ) El detalle genial aquí es que no necesitas cambiar tu función vista para nada. descubre que ambas son casos especícos de Una vista que muestra una lista de objetos. donde el tipo de objeto es variable.1. Con esto en mente. {'month': 'jan'.urls. con URLs tales como: /mydata/jan/01/ /mydata/jan/02/ /mydata/jan/03/ # . person_name) Puedes aplicar la misma losofía a tus vistas Django usando los parámetros extra de URLconf. El truco entra en juego cuando quieres agregar otra URL que usa my_view pero cuya URL no incluye un Por ejemplo. %s' % (greeting.10. month ni/o un day. day): # . ) Y la *signature* de la función vista se vería así: def my_view(request. %s' % person_name podemos extraer el saludo para convertirlo en un parámetro: def greet(person_name. /mydata/birthday/.event_list). month.no es nada que no hayamos visto antes. TRUCOS DE URLCONF 97 Por ejemplo. month y day -. podrías querer agregar otra URL.my_view). A la función vista sólo le incumbe el obtener los parámetros extra. Por ejemplo. Puedes sacar provecho de opciones extra de las URLconf de la siguiente forma: urlpatterns = patterns(''. views. views.. revisión 789 del 9 de noviembre de 2009 . /mydata/dec/30/ /mydata/dec/31/ Esto es lo sucientemente simple de manejar -. %s' % person_name def say_goodbye(person_name): print 'Goodbye. 'day': '06'}). greeting): print ' %s. views.no importa si los mismos provienen de la captura de la URL o de parámetros Convirtiendo una vista en genérica Es una buena práctica de programación el factorizar para aislar las partes comunes del código. (r'^events/$'. views. (r'^mydata/birthday/$'.. Este enfoque es simple y directo -. # urls.conf. (r'^mydata/(?P<month>\w{3})/(?P<day>\d\d)/$'.

estamos pasando a la plantilla el nombre de variable genérico blogentry_list o object_list.html' % model.py from django.lower() para determinar el nombre de la plantilla. Nos ocupamos de las vistas genéricas incluidas con Django en el próximo capítulo. Esta característica es útil en de la clase momentos como este. views urlpatterns = patterns(''.conf.shortcuts import render_to_response from mysite. de repente.py from django. y habla como un pato. model): obj_list = model. views.defaults import * from mysite import models.objects. el model. BlogEntry def event_list(request): obj_list = Event.__name__.entry_list). Podemos fácilmente event_list.objects.objects.object_list.all() template_name = 'mysite/ %s_list. # views.shortcuts import render_to_response def object_list(request. Por __name__ BlogEntry es la cadena BlogEntry. En una sutil diferencia entre este ejemplo y el ejemplo previo.98 CAPÍTULO 10.__name__.all() return render_to_response('mysite/blogentry_list. De ahora en adelante.all() es un ejemplo de tipado de pato (duck typing ): Si camina como un pato. {'event_list': obj_list}) def entry_list(request): obj_list = BlogEntry. ) # views. revisión 789 del 9 de noviembre de 2009 . El diccionario de opciones extra de ULconf puede pasar cualquier tipo de objetos Python -. (r'^blog/entries/$'.BlogEntry}).html'.html'.all() return render_to_response('mysite/event_list. {'model': models. {'model': models. pero hemos dejado eso como un cambiar este nombre de variable a ejercicio para el lector. el cual all(). Cada clase __name__ que retorna el nombre de la clase.py from django. el único requerimiento es que model tenga un atributo objects.Event}).lower() return render_to_response(template_name. cada vez que necesitemos una lista que muestre una listado de objetos. VISTAS AVANZADAS Y URLCONFS ) (r'^blog/entries/$'. {'object_list': obj_list}) Con esos pequeños cambios tenemos. Django incluye un conjunto de vistas genéricas que usan justamente esta técnica para ahorrarte tiempo.objects. A continuación. un par de notas acerca de lo que hicimos: Estamos pasando las clases de modelos directamente.urls. views.models import Event. views. Debido a que los sitios Web impulsados por bases de datos tienen varios patrones comunes. Nota que el código no conoce de qué tipo de objeto se trata a su vez tiene un método Estamos usando model. Python tiene un atributo ejemplo. La línea model. como el parámetro model. una vista reusable e independiente del modelo. podemos tratarlo como un pato. podemos simplemente reusar esta vista object_list en lugar de escribir código de vista. Refactoricemos el código para extraer el tipo de objetos que muestran: # urls.no sólo strings. {'entry_list': obj_list}) Ambas vistas hacen esencialmente lo mismo: muestran una lista de objetos. cuando no conocemos el tipo de clase hasta el momento de la ejecución.object_list. (r'^events/$'.

.conf.1. template_name): var = do_something() return render_to_response(template_name.html'): var = do_something() return render_to_response(template_name. ambos patrones de URL apuntan a la misma vista -de la URL. Por ejemplo. Este ejemplo implementa una pequeña mejora al ejemplo de la sección  Pasando opciones de conguración a una vista: provee un valor por omisión para template_name: def my_view(request.page). es una buena idea agregar puntos de extensión a tus vistas para las opciones de conguración que piensas que la gente pudiera desear cambiar.my_view. Los lectores atentos notarán que en este caso es una pérdida de tiempo y de tipeo capturar traemos a colación sólo para ayudarte a evitar el cometer ese error. TRUCOS DE URLCONF 99 Pasando opciones de conguración a una vista Si estás distribuyendo una aplicación Django.pero el primer patrón no captura nada page() usará su argumento por omisión para num. Esto le indica a la vista qué valor usar para un parámetro por omisión si es que no se especica ninguno. Puedes usar los parámetros extra de URLconf para este n.defaults import * urlpatterns = patterns(''. si tu URLconf captura una variable de grupo con nombre y un parámetro extra de URLconf incluye una variable con el mismo nombre. views.defaults import * urlpatterns = patterns(''. {'var': var}) Entendiendo la precedencia entre valores captuardos vs. "1". num="1"): # Output the appropriate page of blog entries. opciones extra Cuando se presenta un conicto.urls. page() usará el valor de views.conf. se usará el parámetro extra de la URLconf. {'var': var}) revisión 789 del 9 de noviembre de 2009 . regular. porque su valor será siempre descartado en favor del valor proveniente del diccionario. tanto la expresión regular como el diccionario extra incluye un Esto signica que cualquier petición (por ej. template_name='mysite/my_view. analicemos esta URLconf: from django. ) # views. es probable que tus usuarios deseen cierto grado de conguración. (r'^blog/page(?P<num>\d+)/$'.page). como explicamos previamente. views. En otras palabras.py from django. # . Si el primer patrón es disparado.10. Veamos un ejemplo: # urls. los parámetros extra de la URLconf tiene precedencia sobre los parámetros capturados.. lo 10. /mydata/2/ o id.7. /mydata/432432/) serán tratados como si id estuviera id en la expresión independientemente del valor capturado en la URL.1. Tiene precedencia el id jo especicado. En este caso. Una parte de una aplicación que normalmente se hace congurable es el nombre de la plantilla: def my_view(request. Usando argumentos de vista por omisión Otro truco cómodo es el de especicar parámetros por omisión para los argumentos de una vista.py def page(request. (r'^mydata/(?P<id>\d+)/$'. ) Aquí. views.urls. according to num. Aquí. Si el num que se haya capturado mediante la expresión regular. jado a 3.page -. Es común usar esta técnica en combinación con opciones de conguración. {'id': 3}). la función segundo patrón es disparado. (r'^blog/$'. Esto es correcto.

main. '9') Traceback (most recent call last): .auth. no un entero. Manejando vistas en forma especial En algunas ocasiones tendrás un patrón en tu URLconf que maneja un gran número de URLs....9.main. etc.date(1993. 'django. sin importar qué tipo de coincidencia se haya producido con la expresión regular.archive() será una cadena.la misma no muestra todos los campos del formulario. Podríamos resolver este problema tratando esto como un caso especial en la vista.8. 'django.add_stage'). En este caso. 'django. ('^([^/]+)/([^/]+)/add/$'. # .admin..year.contrib. una petición de /auth/user/add/ será manejada por la vista user_add_stage. # . este error se vería así: revisión 789 del 9 de noviembre de 2009 .1. ) Con esto.views.. # .. Sin embargo. ('^auth/user/add/$'. de esta manera: Esto se disparará con URLs como /myblog/entries/add/ y /auth/groups/add/.1.. la página agregar def add_stage(request.views.admin. 10. VISTAS AVANZADAS Y URLCONFS 10. Una manera más elegante sería la de hacer uso del hecho que las URLconfs se procesan desde arriba hacia abajo: urlpatterns = patterns(''. 7.year_archive).contrib. coincide primero con el patrón ubicado más arriba. muestra dos campos de contraseña.. el argumento year de views. ('^([^/]+)/([^/]+)/add/$'. Un error común es intentar crear un objeto datetime.user_add_stage').date con valores de cadena en lugar de valores enteros: >>> import datetime >>> datetime. Por ejemplo en esta línea de URLconf: (r'^articles/(?P<year>\d{4})/$'. app_label. 9) datetime.date('1993'.. (Esto es lógica de corto circuito).contrib. saca provecho de la forma lineal en la que son procesadas la URLconfs y coloca el caso especial primero..100 CAPÍTULO 10.admin. Muchas funciones incluidas con Python son exigentes (y eso es bueno) acerca de aceptar objetos de cierto tipo. Capturando texto en URLs Cada argumento capturado es enviado a la vista como una cadena Python.views. # . las páginas agregar un objeto en el sitio de administración de Django están representadas por la siguiente línea de URLconf: urlpatterns = patterns(''. views. Por ejemplo. ) de un objeto usuario (/auth/user/add/) es un caso especial -. '7'. model_name): if app_label == 'auth' and model_name == 'user': # do special-case code else: # do normal code pero eso es poco elegante por una razón que hemos mencionado en múltiples oportunidades en este capítulo: Coloca lógica de URLs en la vista. pero necesitarás realizar un manejo especial en una de ellas.date(1993. 9) Traducido a una URLconf y una vista. 7.add_stage'). TypeError: an integer is required >>> datetime. Es importante tener esto presente cuando estás escribiendo código de vistas. Aunque dicha URL coincide con el segundo patrón. aun cuando \d{4} sólo coincidirá con cadenas que representen enteros.

month.urls')).blog.defaults import * urlpatterns = patterns(''. (r'^articles/(\d{4})/(\d{2})/(\d{2})/$'.2. El método de la petición (por ej.defaults import * urlpatterns = patterns(''. 10. ) # views.example. include('mysite. 'mysite.date(year.10. Esto no incluye los parámetros de dominio. En otras palabras. day) date = datetime. INCLUYENDO OTRAS URLCONFS 101 # urls. (r'^photos/'.com/myapp/?page3 Django tratará de buscar una coincidencia para myapp/. esta es la URLconf $ include().about').date(int(year). day) En cambio day_archive puede ser escrito correctamente de la siguiente forma: def day_archive(request.com/myapp/ Django tratará de encontrar una coincidencia myapp/.//www.urls: from django. year. Por ejemplo. int(month). Django intenta comparar los patrones de la URLconf con la URL solicitada como una cadena Python normal (no como una cadena Unicode). include('mysite.urls. Es responsabilidad de una función vista el manejar de maneras distintas en base al método de la petición. 10. Continuando con este ejemplo. todos los métodos serán encaminados hacia la misma función para la misma URL.defaults import * revisión 789 del 9 de noviembre de 2009 . elimina todo el fragmento de la URL que ya ha coincidido hasta ese momento y envía la cadena restante a la URLconf mysite.photos. month. views. en una petición de para GET o POST o el nombre del http://www. (r'^weblog/'.10. esta URLconf incluye otras URLconfs: from django.blog. pero estamos evitando ese error en este caso porque la expresión regular en nuestra URLconf ya se ha asegurado que sólo se pasen a la función vista cadenas que contengan dígitos.conf. en cualquier punto. GET. POST.conf.views.urls. HEAD) no se tiene en cuenta cuando se recorre la URLconf. la expresión regular que apunta a un include() no tiene un (carácter que coincide con un n de cadena) pero si incluye una barra al nal. Entendiendo dónde busca una URLconf Cuando llega una petición.py import datetime def day_archive(request.2. ) Existe aquí un detalle importante: en este ejemplo. Cuando Django encuentra incluida para su procesamiento subsecuente. En una petición de http. Tampoco incluye la barra inicial porque toda URL tiene una barra inicial.urls. day) # The following statement raises a TypeError! date = datetime. int(day)) Notar que int() lanza un ValueError cuando le pasas una cadena que no está compuesta únicamente de dígitos. incluir otros módulos URLconf.conf.urls')). month. en esencia. Esto se trata. year. Por ejemplo. Tu URLconf puede. Incluyendo otras URLconfs Si tu intención es que tu código sea usando en múltiples sitios implementados con Django.1. debes considerar el organizar tus URLconfs en una manera que permita el uso de inclusiones.day_archive).py from django. de enraizar un conjunto de URLs debajo de otras. (r'^about/$'.example.

a todas las 10.defaults import * urlpatterns = patterns(''.urls. 'mysite. con independencia de si la vista de la línea realmente acepta esos parámetros como válidos. Cómo trabajan los parámetros capturados con include() Una URLconf incluida recibe todo parámetro que se haya capturado desde las URLconf padres.urls. Debido a que es un include(). URLconf.defaults import * urlpatterns = patterns(''. 'mysite. Django quita todo el texto coinciente.2.views. Conjunto uno: # urls.blog. La parte restante de la URL es /2007/ (con una barra inicial).como un diccionario. Cómo funcionan las opciones extra de URLconf con include() De manera similar. (r'^blog/'.blog.views.urls. Debido a que es un include().conf. por lo tanto.blog. (r'^(\d\d\d\d)/$'. (r'^(\d\d\d\d)/(\d\d)/$'. la variable capturada funciones vista en dicha URLconf.urls.conf. Notar que los parámetros capturados serán pasados siempre a todas las líneas en la URLconf incluida. Cuando haces esto. VISTAS AVANZADAS Y URLCONFS urlpatterns = patterns(''.1.102 CAPÍTULO 10. los siguientes dos conjuntos de URLconfs son funcionalmente idénticos. include() así como puedes pasar opciones extra de URLconf a una vista normal -. 'foo.urls. veremos aquí cómo serían manejadas algunas peticiones de ejemplo: /weblog/2007/: En la primera URLconf. puedes pasar opciones extra de URLconf a las líneas en la URLconf incluida.py from django.about en la primera include() con patrones no include().defaults import * urlpatterns = patterns(''.conf. ) # foo/urls/blog. /about/: Esto coincide con el patrón de la vista mostrando que puedes combinar patrones mysite. que en este caso es /weblog//2007/: En la primera URLconf.views.2. por ejemplo: # root urls.py from django. Django quita todo el texto coincidente. de- 10. ) revisión 789 del 9 de noviembre de 2009 . {'blogid': 3}).py from django.views. el patrón r'^weblog/' coincide. include('inner'). la cual coincide con la primera línea en la URLconf mysite.views. ) En este ejemplo. 'foo. include('foo. (r'^$'.2. la cual no coincide con ninguna de la líneas en la URLconf mysite. (r'^(?P<username>\w+)/blog/'. el patrón r'^weblog/' coincide.blog.year_detail').month_detail'). las opciones extra serán pasadas a todas Por ejemplo. username() es pasada a la URLconf incluida y. 'weblog/'. que en este caso es weblog/.urls.blog_archive').blog')). La parte restante de la URL es 2007/. (r'^archive/$'. Por esta razón esta técnica solamente es útil si estás seguro de que cada vista en la URLconf incluida acepta los parámetros que estás pasando.blog_index'). ) Con esas dos URLconfs.

(r'^archive/$'. include('inner')).conf.3. sin importar de si la vista de la línea realmente acepta esas opciones como válidas.archive').archive'.py from django. 'mysite.views. (r'^about/$'.about').views. {'blogid': 3}).urls. 'mysite. 'mysite. ¾Qué sigue? Uno de los principales objetivos de Django es reducir la cantidad de código que los desarrolladores deben escribir y en este capítulo hemos sugerido formas en las cuales se puede reducir el código de tus vistas y URLconfs. ) # inner. ) Conjunto dos: # urls. ) Como en el caso de los parámetros capturados (sobre los cuales se explicó en la sección anterior).3.urls. 'mysite.defaults import * urlpatterns = patterns(''. (r'^rss/$'.rss').views. (r'^blog/'.views.py from django. (r'^about/$'.rss'. (r'^archive/$'. ¾QUÉ SIGUE? 103 # inner.views.urls.about'.10. 'mysite. {'blogid': 3}).views. 'mysite.conf. Por eta razón esta técnica es útil sólo si estás seguro que todas las vistas en la URLconf incluida acepta las opciones extra que estás pasando. (r'^rss/$'. Ese es el tópico del `próximo capítulo`_.defaults import * urlpatterns = patterns(''.py from django. revisión 789 del 9 de noviembre de 2009 . Duplicate explicit target name: próximo capítulo. {'blogid': 3}).conf. El próximo paso lógico en la reducción de código es eliminar completamente la necesidad de escribir vistas.defaults import * urlpatterns = patterns(''. las opciones extra se pasarán siempre a todas las líneas en la URLconf incluida. 10.

VISTAS AVANZADAS Y URLCONFS revisión 789 del 9 de noviembre de 2009 .104 CAPÍTULO 10.

Presentar objetos basados en fechas en páginas de archivo de tipo día/mes/año. ('^about/$'. una vista sin código! --. Los archivos por día. es en realidad exactamente lo mismo que los ejemplos en el Capítulo 8: la vista del diccionario de parámetros extra y usa esa información cuando renderiza la vista.conf. el desarrollo Web es aburrido y monótono.½mira. djangoproject. Hasta aquí. Una página de evento simple es un ejemplo de lo que 11. Usar vistas genéricas Todas estas vistas se usan creando diccionarios de conguración en tus archivos URLconf y pasando estos diccionarios como el tercer miembro de la tupla URLconf para un patrón dado. Por lo tanto el modelo en cuestión puede ser pasado como un argumento extra a la URLconf. estas vistas proveen interfaces fáciles para realizar las tareas más comunes que encuentran los desarrolladores. hemos cubierto cómo Django trata de alejar parte de esa monotonía en las capas del modelo y las plantillas.Capítulo 11 Vistas genéricas De nuevo aparece aquí un tema recurrente en este libro: en el peor de los casos.defaults import * from django. de modo que puedas escribir rápidamente vistas comunes de datos sin que tengas que escribir mucho código.con o sin autorización.urls. como mostrar una lista de objetos. De hecho. Las vistas llamamos vista detallada. pero los desarrolladores Web también experimentan este aburrimiento al nivel de las vistas. y escribir código que muestra una lista de cualquier objeto. y las páginas más recientes. Las vistas genéricas de Django fueron desarrolladas para aliviar ese dolor. event_list y entry_list del Capítulo 8 son ejemplos de vistas de listado. su detalle asociado. como lo estarían los típicos archivos de un periódico.1. Agrupadas. actualizar y borrar objetos -. Éstas recogen ciertos estilos y patrones comunes encontrados en el desarrollo de vistas y los abstraen.views.html' }) ) Aunque esto podría verse un poco mágico a primera vista -. direct_to_template simplemente toma información revisión 789 del 9 de noviembre de 2009 .com/weblog/) están construidos con ellas. casi todos los ejemplos de vistas en los capítulos precedentes pueden ser reescritos con la ayuda de vistas genéricas. Para repasar.simple import direct_to_template urlpatterns = patterns(''.generic. Por ejemplo. Permitir a los usuarios crear. direct_to_template. El Capítulo 8 rerió brevemente sobre cómo harías para crear una vista genérica. ésta es una URLconf simple que podrías usar para presentar una página estática about (acerca de): from django. podemos reconocer ciertas tareas comunes. Mostrar páginas de listado y detalle para un solo objeto. año del Weblog de Django (http://www. { 'template': 'about. mes. Django viene con vistas genéricas para hacer lo siguiente: Realizar tareas sencillas comunes: redirigir a una página diferente y renderizar una plantilla dada.

escribimos la vista about_pages: from django. { 'template': 'about.views. 11.simple import direct_to_template from mysite.Model): name = models.es una función de vista regular como cualquier otra. Usaremos el objeto 5: Publisher del Capítulo class Publisher(models.CharField(maxlength=50) city = models.generic.CharField(maxlength=60) revisión 789 del 9 de noviembre de 2009 . Ya que es una tarea tan común. Ya que esta devuelve una HttpResponse. TemplateDoesNotExist podemos retornarlo así como está.2.html" % bilidad de recorrido de directorio una vulnerabilidad? No exactamente.defaults import * from django. extendamos nuestro ejemplo about para mapear URLs de la forma primero modicando la URLconf para que apunte a una función de vista: /about/<cualquiercosa>/ para renderizar estáticamente /about/<cualquiercosa>. Como ejemplo. Haremos esto from django. VISTAS GENÉRICAS Ya que esta vista genérica -. about_pages).html" % page) except TemplateDoesNotExist: raise Http404() Aquí estamos tratando direct_to_template como cualquier otra función. podemos reusarla dentro de nuestras propias vistas.conf. por lo tanto atrapamos las excepciones y en su lugar devolvemos errores 404. no todos los valores serán aceptados.template import TemplateDoesNotExist from django.generic.urls. pero las vistas genéricas de Django brillan realmente La vista genérica cuando se trata de presentar vistas del contenido de tu base de datos. ) A continuación.books. ¾Pero es realmente page es page podría causar un recorrido de tomado de la URL solicitada.CharField(maxlength=30) address = models.html. Demos un vistazo a una de estas vistas genéricas: la vista object list.http import Http404 from django. un valor creado maliciosamente de directorio.views. A primera vista. cualquier caracter malicioso (puntos y barras. template="about/ %s. pero aunque de la URL y 11 (discutida en detalle en el `Capítulo page). ¾Hay una vulnerabilidad de seguridad aquí? Los lectores atentos pueden haber notado un posible agujero de seguridad: estamos construyendo el nombre de la plantilla usando contenido interpolado proveniente del navegador (template="about/ %s. ('^about/(w+)/$'. No queremos que una plantilla inexistente cause un error de servidor.views import about_pages urlpatterns = patterns(''. Por lo tanto. Django viene con un puñado de vistas genéricas incluidas que hacen la generación de vistas de listado y detalle de objetos increíblemente fácil.simple import direct_to_template def about_pages(request.html' }). esto parece como una clásica vulnera- 19`_).y todas las otras -. La única ligera dicultad aquí es ocuparse de las plantillas perdidas. direct_to_template. page): try: return direct_to_template(request. Vistas genéricas de objetos direct_to_template ciertamente es útil.106 CAPÍTULO 11. ('^about/$'. Sí. en este caso) serán rechazadas por el sistema de resolución de URLs antes de alcanzar la vista en sí. La clave esta en la URLconf: estamos usando la expresión regular \w \w+ para vericar la parte page sólo acepta letras y números.

usaremos la URLconf bajo estas líneas: from django. EXTENDER LAS VISTAS GENÉRICAS 107 state_province = models.la parte books proviene del nombre de la aplicación que dene el modelo. la pregunta más común que se hacen los nuevos desarrolladores de Django es cómo hacer que las vistas genéricas manejen un rango más amplio de situaciones.all(). Sin embargo. pero en la ausencia de una plantilla explícita Django inferirá una del nombre del objeto.html" -.CharField(maxlength=30) country = models.CharField(maxlength=50) website = models. la plantilla inferida será del modelo.models import Publisher publisher_info = { "queryset" : Publisher. sin embargo. El Apéndice D documenta todas las vistas genéricas y todas sus opciones en detalle.views.books.objects.defaults import * from django. el resto de este capítulo considerará algunas de las maneras comunes en que tú puedes personalizar y extender las vistas genéricas.conf.name class Meta: ordering = ["-name"] class Admin: pass Para construir una página listado de todos los books. Esta plantilla será renderizada en un contexto que contiene una variable llamada todos los objetos book. Estas situaciones usualmente recaen en un puñado de patrones que se tratan en las secciones que siguen. list_detail. hay maneras de simplemente extender las vistas genéricas para manejar un conjunto más amplio de casos de uso. Extender las vistas genéricas No hay duda de que usar las vistas genéricas puede acelerar el desarrollo sustancialmente.3.URLField() def __str__(self): return self. en casi cada uno de estos casos. Todas las geniales características de las vistas genéricas provienen de cambiar el diccionario info pasado a la vista genérica. llega un momento en el que las vistas genéricas no son sucientes.generic import list_detail from mysite. En la mayoría de los proyectos.name }}</li> { % endfor %} </ul> { % endblock %} Eso es realmente todo en lo referente al tema. publisher_info) ) Ese es todo el código Python que necesitamos escribir. todavía necesitamos escribir una plantilla. revisión 789 del 9 de noviembre de 2009 .11.3. Una plantilla muy simple podría verse como la siguiente: object_list que plantilla debe usar incluyendo una clave template_name "books/publisher_list. En este caso. (r'^publishers/$'. mientras que la parte publisher es sólo la versión en minúsculas del nombre object_list la cual contiene { % extends "base. } urlpatterns = patterns(''.urls. Afortunadamente. 11.object_list. De hecho. Podríamos decirle explícitamente a la vista en el diccionario de argumentos extra.html" %} { % block content %} <h2>Publishers</h2> <ul> { % for publisher in object_list %} <li>{{ publisher.

Es muy práctico.all(). no es una forma amistosa para los autores de plantillas: ellos sólo object_list.2. Tus compañeros de trabajo que diseñan las plantillas te lo agradecerán.objects.3. para proporcionar la lista de todos los publishers en la vista de detalles. Ya que Django sabe que ese QuerySet en particular nunca debe ser almacenado en la caché. Este es un diccionario de objetos extra que serán agregados al contexto de la plantilla. Puedes hacer esto con una función explícitamente denida: def get_books(): return Book. Tal vez hayas notado que el ejemplo de la plantilla publisher list almacena todos los books en una variable llamada tienen que saber aquí que están trabajando con books. Cualquier callable 13 (por ejemplo. Por lo tanto. notarás que la vista genérica no reeja estos cambios hasta que reinicias el servidor Web (mira Almacenamiento en caché y QuerySets en el Apéndice C para mayor información sobre cuándo los QuerySets son almacenados en la cache y evaluados). "template_object_name" : "publisher". VISTAS GENÉRICAS 11. La vista genérica object_detail provee el publisher al contexto.all() publisher_info = { "queryset" : Publisher. "template_object_name" : "publisher".3.1.objects. "extra_context" : {"book_list" : Book. la vista genérica se hace cargo de limpiar la caché cuando cada vista es renderizada. pero parece que no hay forma de obtener una lista de todos los publishers en esa plantilla.all(). Este patrón puede ser usado para pasar extra_context son evaluadas. Debido a que este ejemplo coloca cualquier información hacia la plantilla para la vista genérica. Pero sí la hay: todas las vistas genéricas toman un parámetro opcional extra. Una vez que agregues o elimines publishers. extra_context. sólo se evaluará una vez (cuando la URLconf se cargue por primera vez). "extra_context" : {"book_list" : get_books} } revisión 789 del 9 de noviembre de 2009 .all()} } Esto llenaría una variable {{ book_list }} en el contexto de la plantilla. Crear contextos de plantilla amistosos Aunque que esto funciona bien.objects.all(). Por ejemplo. Agregar un contexto extra A menudo simplemente necesitas presentar alguna información extra aparte de la proporcionada por la vista genérica. (r'^publishers/$'. en realidad hay un error sutil aquí -. template_object_name: publisher_info = { "queryset" : Publisher. una cuando su vista sea renderizada (en vez de sólo la primera vez). usamos un diccionario info como el que sigue: publisher_info = { "queryset" : Publisher. Un nombre mejor para esa variable sería el contenido de esa variable es bastante obvio. list_detail. publisher_info) ) Proveer un template_object_name útil es siempre una buena idea. "template_object_name" : "publisher".objects.object_list.¾puedes detectarlo? El problema aparece cuando las consultas en Publisher.objects.all() en la URLconf. Nota Este problema no se aplica al argumento queryset de las vistas genéricas. 11. } urlpatterns = patterns(''. Podemos cambiar el nombre de esa variable fácilmente con el argumento publisher_list.108 CAPÍTULO 11. Sin embargo.objects. piensa en mostrar una lista de todos los otros publisher en cada página de detalle de un publisher. La solución es usar un callback función) que sea pasado a extra_context extra_context será evaluado 12 en en vez de un valor.

Mostrar subconjuntos de objetos Ahora echemos un vistazo más de cerca a esta clave de las vistas genéricas usan uno de estos argumentos Apéndice C para los detalles completos). ) Este es un muy lindo y simple ejemplo.objects.objects.3.object_list. tú usualmente querrás hacer más que sólo reordenar objetos. Las vistas genéricas tienen un allow_empty para este caso. verica que en realidad tienes un Publisher con el nombre 'Apress Publishing'. y mira el Para tomar un ejemplo simple.object_list. list_detail. (r'^publishers/$'. puedes usar la misma técnica: apress_books = { "queryset": Book. ) Nota que además de un queremos.all es publisher_info = { "queryset" : Publisher.3.order_by("-publication_date"). . Mira el Apéndice D para mayores detalles. empezamos escribiendo una URLconf. Como siempre. Por supuesto. book_info).object_list. pero ilustra bien la idea.objects.all. para estar seguro.4. pero ¾qué pasa si queremos escribir una vista 16 la vista genérica object_list que muestre todos los books por algún publisher arbitrario?.all().filter(publisher__name="Apress Publishing"). revisión 789 del 9 de noviembre de 2009 Otra necesidad común es ltrar los objetos que se muestran en una página listado por alguna clave en la URLconf. tal vez querríamos ordenar una lista de books por fecha de publicación. queryset que hemos venido usando hasta aquí. "template_name" : "books/apress_list. "extra_context" : {"book_list" : Book. Si quieres presentar una lista de books de un publisher particular. queryset ltrado. EXTENDER LAS VISTAS GENÉRICAS 109 o puedes usar una versión menos obvia pero más corta que se basa en el hecho de que en sí un callable: Publisher.objects. list_detail.objects.html" } urlpatterns = patterns(''. (r'^books/apress/$'. necesitamos otro puñado de líneas en la URLconf. Book.object_list. La mayoría queryset -.es la manera en que la vista conoce qué conjunto de objetos mostrar (mira Seleccionando objetos en el Capítulo 5 para una introducción a los QuerySets. esto hace referencia a la función sin invocarla realmente 11. y más de unos pocos publishers no será razonable. } urlpatterns = patterns(''. con el más reciente primero. Filtrado complejo con funciones adaptadoras Anteriormente codicamos 15 el nombre del publisher en la URLconf. también estamos usando un nombre de plantilla personalizado. También nota que ésta no es una forma muy elegante de hacer publisher-specic books. apress_books). list_detail.objects. que puede no ser lo que Nota Si obtienes un error 404 cuando solicitas parámetro /books/apress/. 11.11.3. la vista genérica usaría la misma plantilla que la lista de objetos genérica 14 . publisher_info). (r'^publishers/$'. Si no lo hiciéramos. (r'^books/$'.all(). book_info = { "queryset" : Book. "template_object_name" : "publisher".3. Enfrentaremos este problema en la siguiente sección. list_detail. Podemos encapsular para evitar escribir mucho código a mano. publisher_info). Si queremos agregar otra página publisher.all} } Nota la falta de paréntesis después de (cosa que hará la vista genérica luego).

. las vistas genéricas esperan un cierto conjunto de argumentos y retornan objetos HttpResponse.books. extra_context = {"publisher" : publisher} ) Esto funciona porque en realidad no hay nada en especial sobre las vistas genéricas -. mira la siguiente sección) de pasarle el control a la vista genérica. Por lo tanto. escribiremos la vista books_by_publisher: from django.get(name__iexact=name) except Publisher. pero una vez más fácilmente podríamos escribir una vista personalizada para mantener ese campo Primero.models import Book. list_detail. (r'^publishers/$'.books. books_by_publisher). try: publisher = Publisher. (r'^books/(w+)/$'.views import author_detail urlpatterns = patterns(''. 11.views. ) A continuación.models import Author revisión 789 del 9 de noviembre de 2009 .son sólo funciones Python.html".110 CAPÍTULO 11. por supuesto.5. Realizar trabajo extra El último patrón común que veremos involucra realizar algún trabajo extra antes o después de llamar a la vista Imagina que tenemos un campo last_accessed en nuestro objeto registro de la última vez que alguien vio ese author. Nota Nota que en el ejemplo anterior pasamos el publisher que se está mostrando actualmente en el extra_context. template_object_name = "books". #.objects. Como cualquier función de vista. template_name = "books/books_by_publisher. Publisher def books_by_publisher(request.object_list.filter(publisher=publisher).generic import list_detail from mysite. le permite a la plantilla saber qué objeto padre esta siendo navegado en ese momento..books. no sabría nada sobre este campo. queryset = Book. return list_detail. publisher_info). genérica. ) Luego escribiremos nuestra función wrapper: import datetime from mysite. name): # Look up the publisher (and raise a 404 if it can't be found). Author que estuvimos usando para tener un object_detail. La vista genérica actualizado.DoesNotExist: raise Http404 # Use the object_list view for the heavy lifting. Esto es usualmente una buena idea en wrappers de esta naturaleza. necesitamos agregar una pequeña parte de detalle sobre el author en la URLconf para que apunte a una vista personalizada: from mysite.http import Http404 from django. VISTAS GENÉRICAS urlpatterns = patterns(''. (r'^authors/(?P<author_id>d+)/$'.object_list( request. es increíblemente fácil encapsular una pequeña función sobre una vista genérica que realiza trabajo adicional antes (o después.objects.3. author_detail).

datetime. Este arreglo de HttpResponse que pueden ser tratados como Content-Disposition. 11.: hard-coded. podríamos usar una vista como esta: def author_list_plaintext(request): response = list_detail. ¾QUÉ SIGUE? 111 from django. Si quisiéramos proporcionar una versión en texto plano 17 que se pueda descargar desde la lista de autores.last_accessed = datetime. pero las ideas generales presentadas aquí deberían aplicarse a cualquier vista genérica. 14 N.generic import list_detail from django. 11 N.: en texto plano. 17 N.: vanilla object list. 13 N.4.4. del T. object_id = author_id.all(). last_accessed a tu Podemos usar un método similar para alterar la respuesta devuelta por la vista genérica. del T.save() # Show the detail page return list_detail. del T. 15 N.object_list( request.11. Duplicate explicit target name: próximo capítulo.objects.views. template_name = "books/author_list. mostrando todas las maneras geniales en que pueden ser extendidas. pk=author_id) # Record the last accessed date author. El Apéndice D cubre todas las vistas disponibles en detalle.object_detail( request.: wrap.html.txt" ) response["Content-Disposition"] = "attachment.: en Python cualquier objeto que puede ser llamado como función. del T.shortcuts import get_object_or_404 def author_detail(request.: directory traversal vulnerability. ¾Qué sigue? En este capítulo hemos examinado sólo un par de las vistas genéricas que incluye Django. 12 N. filename=authors. En el `próximo capítulo`_ ahondamos profundamente en el funcionamiento interno de las plantillas de Django. revisión 789 del 9 de noviembre de 2009 . y es de lectura obligada si quieres sacar el mayor provecho de esta característica. 16 N.all().: llamada a función. del T. hemos tratado el sistema de plantillas meramente como una herramienta estática que puedes usar para renderizar tu contenido.txt" return response Esto funciona porque la vista genérica devuelve simplemente objetos diccionarios para establecer las cabeceras HTTP. queryset = Author. mimetype = "text/plain". del T. instruye al navegador a descargar y guardar la página en vez de mostrarla en pantalla. Hasta ahora.objects. ) Nota Este código en realidad no funcionará a menos que agregues un campo modelo Author y agregues una plantilla books/author_detail. queryset = Author. del T. author_id): # Look up the Author (and raise a 404 if she's not found) author = get_object_or_404(Author. por otro lado.now() author.

112 CAPÍTULO 11. VISTAS GENÉRICAS revisión 789 del 9 de noviembre de 2009 .

ya sea por si planeas extender el sistema.2.template. sin el resto del framework. lee la sección  `Congurando mismo capítulo. vamos a recordar algunos términos presentados en el Capítulo 4: Una plantilla es un documento de texto. o para hacer tu trabajo más fácil de alguna otra manera. Por ejemplo. Las etiquetas de variable deben ser rodeadas por {{ y }}: My first name is {{ first_name }}. necesita un contexto. Procesadores de contexto pero Django también provee una subclase especial: revisión 789 del 9 de noviembre de 2009 django. Revisión del lenguaje de plantillas Primero. Una etiqueta de bloque es un símbolo dentro de una plantilla que hace algo. o por si sólo eres curioso acerca de su funcionamiento.así sea para agregar funcionalidad. debemos dar una mirada a algunos conceptos internos que quedaron fuera del Capítulo 4 por simplicidad. o un string normal de Python marcado con la sintaxis especial del lenguaje de plantillas de Django. Las etiquetas de bloque deben ser rodeadas por {% y %}: { % if is_logged_in %} Thanks for logging in! { % else %} Please log in. Aunque primero.RequestContext . Si estás tratando de utilizar el sistema de plantillas de Django como parte de otra aplicación. { % endif %} Una variable es un símbolo dentro de una plantilla que emite un valor. Usualmente este contexto es una instancia de django. Cuando una plantilla debe ser renderizada.1. Esta denición es así de vaga a propósito. Una plantilla puede contener etiquetas de bloque (block tags ) y variables. Este capítulo se adentra en el sistema de plantillas de Django. probablemente quieras algunas veces modicar y extender el sistema de plantillas -. obtener contenido de la base de datos.Capítulo 12 Extender el sistema de plantillas Aunque la mayor parte de tu interacción con el sistema de plantillas (templates ) de Django será en el rol de autor. una etiqueta de bloque puede producir contenido. My last name is {{ last_name }}. referirse al Capítulo 4. Un contexto es un mapeo entre nombres y valores (similar a un diccionario de Python) que es pasado a una plantilla. El resto de este capítulo discute las distintas maneras de extender el sistema de plantillas. 12. o habilitar acceso a otras etiquetas de plantilla.template. el Sistema de plantillas en modo autónomo`_ luego en este 12. servir como estructura de control (una sentencia if o un loop for). cubriendo todo lo que necesitas saber.Context. Para más detalles acerca de estos términos. es decir. Una plantilla renderiza un contexto reemplazando los huecos que dejan las variables por valores tomados del contexto y ejecutando todas las etiquetes de bloque.

' }) return t. user y las.get_template('template4. 'message': 'I am the fourth view. 'message': 'I am the second view..render(c) A propósito no hemos usado el atajo pasos necesarios. t = loader.. 'user': request.cosas como el objeto Usa RequestContext agrega muchas variables al contexto de nuestra plantilla HttpRequest o información acerca del usuario que está siendo usado actualmente. 'user': request.get_template('template3. Por ejemplo. revisión 789 del 9 de noviembre de 2009 . 'message': 'I am view 1.user. 'ip_address': request. EXTENDER EL SISTEMA DE PLANTILLAS que actúa de una manera levemente diferente. t = loader.META['REMOTE_ADDR'].' }) return t. construimos el contexto y renderizamos las plantillas. RequestContext cuando no quieras especicar el mismo conjunto de variables una y otra vez en una serie de plantillas.get_template('template1.template import loader.META['REMOTE_ADDR'].get_template('template2.a su plantilla.manualmente cargamos las plantilapp.html') c = Context({ 'app': 'My app'. -.user. 'ip_address': request.. 'ip_address': request.render(c) def view_4(request): # ..render(c) def view_3(request): # .html') c = Context({ 'app': 'My app'.user.' }) return t. t = loader. Context def view_1(request): # ....114 CAPÍTULO 12.render(c) def view_2(request): # .html') c = Context({ 'app': 'My app'. 'message': 'I am the third view. t = loader. 'ip_address': request. ¾No sería bueno poder RequestContext y los procesadores de contexto fueron creado para resolver este problema. 'user': request.META['REMOTE_ADDR'].' }) return t. Cada vista pasa las mismas tres variables -eliminar esa redundancia? render_to_response en estos ejemplos -. Simplemente por claridad. Los procesadores de contexto te permiten especicar un número de variables que son incluidas automáticamente en cada contexto -.sin la necesidad de tener que hacerlo manualmente en cada llamada a RequestContext en lugar de Context render_to_response()..META['REMOTE_ADDR'].user. considera estas cuatro vistas: from django.html') c = Context({ 'app': 'My app'. estamos demostrando todos los ip_address -. 'user': request. El secreto está en utilizar cuando renderizamos una plantilla.

t = loader. el cual es una lista o tupla de funciones procesadoras de contexto a utilizar. A continuación mostramos como el ejemplo anterior puede lograrse utilizando procesadores de contexto: from django..'}..get_template('template1.render(c) def view_4(request): # ..user. Ya no es necesario en cada vista incluir diferencias en cuanto a cómo el contexto es construido. processors=[custom_proc]) return t. Hemos cambiado las cuatro vistas para que usen argumento sea una instancia de Dos..get_template render() en la plantilla. processors.. Para demostrar el funcionamiento a revisión 789 del 9 de noviembre de 2009 . pasamos custom_proc. HttpRequest -. Uno.12.la cual fue pasada a la vista en primer lugar (request).template import loader.'}. RequestContext def custom_proc(request): "A context processor that provides 'app'.META['REMOTE_ADDR'] } def view_1(request): # . t = loader.html') c = RequestContext(request. t = loader. cuando construimos el contexto. processors=[custom_proc]) return t. En el Capítulo 4. ya Cada vista aún posee la exibilidad como para introducir una o más variables en el contexto de la plantilla si es necesario.get_template('template2. 'user': request. nuestro procesador RequestContext recibe un parámetro opcional de contexto denido previamente. {'message': 'I am the second view." return { 'app': 'My app'. 'ip_address': request. En este ejemplo. Eso es todo lo que hace. {'message': 'I am the third view. Hay dos requiere que el primer que ahora estas variables son provistas por app. llamar al método render_to_response(). {'message': 'I am the fourth view.'}.html') c = RequestContext(request.html') c = RequestContext(request.render(c) def view_2(request): # . el cual nos ahorra tener que llamar a loader. En este caso.get_template('template3. {'message': 'I am view 1. RequestContext en lugar RequestContext de Context. processors=[custom_proc]) return t. t = loader. la variable de plantilla en cada una de las vistas. Este es un procesador de contexto -. user o ip_address custom_proc. denimos una función custom_proc.render(c) Inspeccionemos paso a paso este código: Primero.render(c) def view_3(request): # .'}. processors=[custom_proc]) return t.2.toma un objeto HttpRequest y devuelve un diccionario con variables a usar en el contexto de la plantilla. 'user' and 'ip_address'. presentamos el atajo luego crear un message es creada de manera diferente Context y ademas..html') c = RequestContext(request. PROCESADORES DE CONTEXTO 115 La forma de nivel más bajo de usar procesadores de contexto es crear algunos de ellos y pasarlos a RequestContext...get_template('template4.

" return { 'app': 'My app'. Es decir. 'django. hemos logrado reducir el código para renderizar las plantillas en cada vista a una sola línea.116 CAPÍTULO 12.html'. processors=[custom_proc])) def view_3(request): # .html'. processors=[custom_proc])) Aquí. estamos especicando una y otra vez nuestro contexto.'}.y preferible -. render_to_response(). pero.. {'message': 'I am the second view. evaluando la concisión de este código. context_instance=RequestContext(request.utilizar los procesadores de contexto junto a mediante el argumento context_instance render_to_response(). return render_to_response('template2. y deestar vuelven un diccionario de items que serán incluidos en el contexto de la plantilla. return render_to_response('template3. 'django. 'ip_address': request.media'.'}.debug'. hasta ahora usar procesadores de contexto no nos ahorra mucho código si tenemos que escribir processors constantemente. context_instance=RequestContext(request.. El parámetro de conguración designa cuales serán los procesadores de contexto que deberán ser aplicados siempre a la necesidad de especicar processors cada vez que utilizamos RequestContext. context_instance=RequestContext(request..auth'.core. debemos admitir que hemos logrado reducir la redundancia en los datos (nuestras variables de plantilla). 'user': request. lo cual signica que estos procesadores deberán en algún lugar dentro de tu PYTHONPATH (para poder referirse a ellos desde el archivo de conguración) revisión 789 del 9 de noviembre de 2009 .context_processors..funciones que toman un objeto HttpRequest como primer argumento.html'.context_processors.core. processors=[custom_proc])) def view_2(request): # .shortcuts import render_to_response from django. Por esta razón. {'message': 'I am the third view.context_processors. EXTENDER EL SISTEMA DE PLANTILLAS bajo nivel de los procesadores de contexto. {'message': 'I am the fourth view. 'django.META['REMOTE_ADDR'] } def view_1(request): # . TEMPLATE_CONTEXT_PROCESSORS tiene.. return render_to_response('template1. Esto es una mejora.context_processors. Django admite el uso de procesadores de contexto globales.'}. pero aun así. ) RequestContext..'}. processors=[custom_proc])) def view_4(request): # . TEMPLATE_CONTEXT_PROC Esto elimina custom_proc Este parámetro de conguración es una tupla de funciones que utilizan la misma interfaz que nuestra función utilizada previamente -. por omisión.i18n'. {'message': 'I am view 1..template import RequestContext def custom_proc(request): "A context processor that provides 'app'. el siguiente valor: TEMPLATE_CONTEXT_PROCESSORS = ( 'django.. en los ejemplos anteriores no hemos utilizado pero es posible -. Esto lo logramos de la siguiente manera: from django.html'. context_instance=RequestContext(request.core.user.core. 'user' and 'ip_address'. Ten en cuenta que los valores en TEMPLATE_CONTEXT_PROCESSORS son especicados como strings. return render_to_response('template4.

request. de lo contrario.2.3. revisión 789 del 9 de noviembre de 2009 .contrib.context_processors. cada RequestContext TEMPLATE_CONTEXT_PROCESSORS Esta variable puede usarse en con- contendrá las siguientes variables: debug: El valor del parámetro de conguración DEBUG (True o False). entonces la segunda sobre-escribirá a la primera. es por eso que dividir la funcionalidad de tu procesador de manera lógica puede ser útil para poder reutilizarlos en el futuro. permisos y mensajes. las plantillas para saber si estás en modo de depuración o no.core. este procesador de contexto sólo agregará las variables al contexto si las dos siguientes condiciones son verdaderas. 12.core. RequestContext contendrá una variable request.request Si este procesador está habilitado. Consideraciones para escribir tus propios procesadores de contexto Algunos puntos a tener en cuenta: Cada procesador de contexto debe ser responsable por la mínima cantidad de funcionalidad posible. la cual representa los permisos que posee el usuario actualmente autenticado. cada RequestContext contendrá las siguientes variables: LANGUAGES: El valor del parámetro de conguración LANGUAGES. Si tiene este procesador.context_processors.get_and_delete_messages() actualmente messages: Una lista de mensajes (como string ) para el usuario actualmente autenticado.2. Detrás para cada request. perms: Instancia de django.12. django. El parámetro de conguración DEBUG es True La solicitud (request ) viene de una dirección IP listada en el parámetro de conguración INTERNAL_IPS.context_processors. entre ellos los que están activos por defecto. sql_queries: Una lista de diccionarios {'sql': . Si ables: django.auth... La lista está ordenada respecto a cuándo fue ejecutada cada consulta. 'time': . es decir.debug Este procesador añade información de depuración a la capa de plantillas.i18n Si este procesador está habilitado. 12.2. PROCESADORES DE CONTEXTO 117 Estos procesadores de contexto son aplicados en orden. 12. Usar muchos procesadores es algo sencillo.5. y luego los borra de la base de datos. django. LANGUAGE_CODE: request. esta variable llama a método colecta los mensajes del usuario.user.2.4. objeto django.1.2. la cual es el actual Este procesador no está habilitado por defecto.2..core.core.. si existe. cada HttpRequest. Este del telón.User representando al usuario AnonymousUser si el cliente no se ha autenticado aún). cada TEMPLATE_CONTEXT_PROCESSORS user: Una instancia de RequestContext contendrá las siguientes vari- autenticado (o una instancia de django.models.context_processors. Django provee un numero de procesadores de contexto simples. el valor del parámetro de cong- En el Apéndice E se especica más información sobre estos parámetros.} representando todas las con- sultas SQL que se generaron durante la petición (request ) y cuánto duraron.2. 12..context_processors.auth contiene este procesador.LANGUAGE_CODE uración LANGUAGE_CODE. si uno de estos procesadores añade una variable al contexto y un segundo procesador añade otra variable con el mismo nombre. Como la información de depuración es sensible. En el Capítulo 12 encontrarás más información acerca de usuarios. 12.PermWrapper.core.

Usará cada uno de los cargadores hasta que uno de los mismos tenga éxito en la búsqueda de la plantilla. la plantilla compilada no existe. TEMPLATE_LOADERS debe ser una tupla de cadenas. Esto signica que puedes almacenar plantillas en tus aplicaciones individuales. 'myproject. Django usa los cargadores de plantillas en el orden en el que aparecen en la variable de conguración TEMPLATE_DIRS. Por ejemplo si ('myproject.html') buscará plantil- • /path/to/myproject/polls/templates/foo.template. excepto que recibe una lista de nombres de plantillas.filesystem.template.polls'.select_template(template_name_list): select_template es similar a get-template. Retorna la primera plantilla de dicha lista que existe. Django buscará una plantilla en el mismo.eggs. INSTALLED_APPS tienen un sub-directorio templates. de acuerdo a TEMPLATE_DIRS.app_directories.template.music') las en el siguiente orden: entonces INSTALLED_APPS contiene get_template('foo. excepto que carga las plantillas desde eggs Python en lugar de hacerlo desde el sistema de archivos.loaders.loader.template.template. se generará django. diremos también que la convención es grabarlos en un archivo llamado TEMPLATE_CONTEXT_PROCESSORS.loaders. Sin embargo. cada una de esas funciones usan por omisión el valor de tu variable de conguración TEMPLATE_LOADERS.3. Por omisión este cargador está desactivado. facilitando la distribución de aplicaciones Django con plantillas por omisión. Para cada aplicación en INSTALLED_APPS.loaders. django. eso.load_template_source: Este cargador es básicamente idéntico a app_directories. Si el directorio existe. No importa dónde residan en el sistema de archivos. Como los nombres de variables son sensibles a mayúsculas/minúsculas no es una mala idea usar mayúsculas para las variables provistas por un procesador. internamente las mismas delegan la tarea pesada a un cargador de plantillas. Algunos de los cargadores están. así que trata de seleccionar nombres de variables con pocas probabilidades de entrar en conicto con nombre de variables que tus plantillas pudieran usar en forma independiente.loader.118 CAPÍTULO 12. django.load_template_source: Este cargador carga plantillas desde aplicaciones Django en el sistema de archivos. pero puedes usar cargadores de plantillas personalizados (custom ) para cargar plantillas desde otros orígenes. Estos son los cargadores de plantillas incluidos con Django: django. EXTENDER EL SISTEMA DE PLANTILLAS Ten presente que cualquier procesador de contexto en TEMPLATE_CONTEXT_PROCESSORS estará disponible en cada plantilla cuya conguración esté dictada por ese archivo de conguración. desactivados pero puedes activarlos editando la variable de conguración Como se vio en el Capítulo 4.load_template_source: Este cargador carga plantillas desde el sistema de archivos. mientras se hallen en tu ruta de Python de manera que puedas incluirlos en tu variable de conguración ubicado en tu aplicación o en tu proyecto.html • /path/to/myproject/music/templates/foo.get_template(template): get_template retorna (un objeto Template) para la plantilla con el nombre provisto. necesitarás activarlo si estás usando eggs para distribuir tu aplicación.html Notar que el cargador realiza una optimización cuando es importado por primera vez: hace caching de una lista de cuales de los paquetes en Por omisión este cargador está activo. TEMPLATE_DIRS para cargar las plantillas. por omisión. Django tiene dos maneras de cargar plantillas: django. donde cada cadena representa un cargador de plantillas. revisión 789 del 9 de noviembre de 2009 . Si la plantilla una excepción TemplateDoesNotExist.py 12. Detalles internos de la carga de plantillas En general las plantillas se almacenan en archivos en el sistema de archivos. Si ninguna de las plantillas existe se lanzará una excepción TemplateDoesNotExist. Por omisión está activo. el cargador busca un sub-directorio templates. Habiendo dicho context_processors.

Library es la estructura de datos en la cual son registradas todas las etiquetas y ltros. Afortunadamente. Esta instancia de template. respectivamente. Una vez que has creado ese módulo Python.4. La mayor parte de la personalización de plantillas se da en forma de etiquetas y/o ltros. decidir qué aplicación Django alojará la biblioteca. views.4. Por ejemplo: Django apropiado. Explicaremos esto un poco más adelante. 12.Library() Nota django/template/defaultfilters.py. probablemente ensamblarás tus propias bibliotecas de etiquetas y ltros que se adapten a tus propias necesidades. revisión 789 del 9 de noviembre de 2009 . La creación de una biblioteca para plantillas es un proceso de dos pasos: Primero. etc. EXTENDER EL SISTEMA DE PLANTILLAS 119 12. No existen límites en Sólo ten presente que una sentencia {% cargará etiquetas/ltros para el nombre del módulo Python provisto. usarás la misma para crear ltros y etiquetas para plantillas. El nombre del segundo archivo es el que usarás para cargar las etiquetas más tarde. no el nombre de la aplicación. crear un directorio encontrarse en el mismo nivel que templatetags en el paquete de aplicación models.py templatetags/ views. el módulo debe contener una variable a nivel del módulo llamada register que sea una instancia de template. Así que inserta en la zona superior de tu módulo. sólo tendrás que escribir un poquito de código Python. o puedes crear otra aplicación con el solo n de alojar la biblioteca.py django/template/defaulttags.py. Extender el sistema de plantillas Ahora que entiendes un poco más acerca del funcionamiento interno del sistema de plantillas. es muy fácil denir tu propia funcionalidad. templatetags. Si has creado una aplicación vía startapp ración manage.py. Crear una biblioteca para plantillas biblioteca Ya sea que estés escribiendo etiquetas o ltros personalizados.1.py (para indicarle a Python que se trata de un paquete que contiene código Python) y un archivo que contendrá tus deniciones personalizadas de etiquetas/ltros. Por ejemplo. echemos una mirada a cómo extender el sistema con código propio.py Crea dos archivos vacíos en el directorio templatetags: un archivo __init__. Aunque el lenguaje de plantillas de Django incluye muchos.Library. dependiendo de si estás escribiendo ltros o etiquetas. Algunas aplicaciones en django. Puedes encontrarlos en y Una vez que hayas creado esta variable Para ver un buen número de ejemplos. Sin importar cual de las dos rutas tomes.py models. la primera tarea a realizar es crear una para plantillas -. si tus etiquetas/ltros personalizadas están en un archivo llamado poll_extras. te permite tener en cierto equipo el código Python de varias bibliotecas para plantillas sin tener que activar el acceso a todas ellas para cada instalación de Django. examina el código fuente de los ltros y etique- register. asegúrate de agregar la aplicación a tu variable de congu- INSTALLED_APPS. Debe books/ __init__.py puedes colocarla allí. Segundo. Si escribes una biblioteca para plantillas que no se encuentra atada a ningún modelo/vista particular es válido y normal el tener un paquete de aplicación Django que sólo contiene un paquete lo referente a cuántos módulos puedes poner en el paquete load %} templatetags. lo siguiente: from django import template register = template. tas incluidos con Django. Se trata de una característica de seguridad.contrib también contienen bibliotecas para plantillas.4.un pequeño fragmento de infraestructura con el cual Django puede interactuar.py.12. Para ser una biblioteca de etiquetas válida. { % load %} entonces deberás escribir lo siguiente en una plantilla: { % load poll_extras %} La etiqueta examina tu variable de conguración INSTALLED_APPS y sólo permite la carga de bibliotecas para plantillas desde aplicaciones Django que estén instaladas.

en el ltro "bar". Escribir ltros de plantilla personalizados Los ltros personalizados son sólo funciones Python que reciben uno o dos argumentos: El valor de la variable (entrada) El valor del argumento. '') revisión 789 del 9 de noviembre de 2009 .4 o más reciente. "Converts a string into all lowercase" return value.lower() Una vez que has escrito tu denición de ltro. No deben arrojar excepciones.filter def lower(value): return value. basta con que no incluyas el argumento en tu función: def lower(value): # Only one argument.filter() tiene dos argumentos: El nombre del ltro (una cadena) La función ltro propiamente dicha Si estás usando Python 2. el cual puede tener un valor por omisión o puede ser obviado. como en el segundo ejemplo. necesitas registrarlo en tu instancia de disponible para el lenguaje de plantillas de Django: Library. dependiendo de qué sea más apropiado. {{ var|foo:"bar" }} el ltro foo recibiría el contenido de la variable var y el argumento Las funciones ltro deben siempre retornar algo. arg): "Removes all values of arg from the given string" return value. '') @register.replace(arg. '') Y este es un ejemplo de cómo se usaría: {{ somevariable|cut:"0" }} La mayoría de los ltros no reciben argumentos. Si existe un error. y deben fallar silenciosamente.Library() @register. Django usará el nombre de la función como nombre cut: from django import template register = template.filter() como un decorador: @register.filter(name='cut') def cut(value. arg): return value.lower() Si no provees el argumento del ltro.filter('cut'. las mismas deben retornar la entrada original o una cadena vacía.filter('lower'. lower) El método Library. puedes usar register. En ese caso.2. que provee el ltro name.120 CAPÍTULO 12.replace(arg. EXTENDER EL SISTEMA DE PLANTILLAS 12.4.filter(name='cut') def cut(value. para que esté register. arg): return value. cut) register. Veamos entonces el ejemplo completo de una biblioteca para plantillas. Por ejemplo.replace(arg. Esta es un ejemplo de denición de un ltro: def cut(value.

split() (el cual usa la semántica natural de Python para dividir strings. usando la sintaxis de ser usada de la siguiente manera: { % current_time %} que visualice la fecha/hora actuales con un formato destrftime (ver http://www.</p> Nota Si. incluyendo aquellos dentro de cadenas entre comillas.4. En nuestro caso. token. ya que eso acoplaría innecesariamente el nombre de la etiqueta a la función. token): try: # split_contents() knows not to split quoted strings. mientras deja unidas a los strings. token. Los resultados son todos concatenados juntos para formar la salida de la plantilla.3.split_contents() separa los argumentos en sus espacios. escribamos una etiqueta terminado por un parámetro pasado a la etiqueta. Cada nodo es una instancia método django. una plantilla compilada es simplemente una plantilla compilada.%d %I: %M %p" %}. y por esto no es tan robusto.%m. En cambio. el intérprete (parser ) de plantillas llama a una función de Python pasándole el contenido de la etiqueta y el objeto parser en sí mismo.%m.split_contents()[0] siempre contendrá el nombre de tu etiqueta -. Cuando Django compila una plantilla. EXTENDER EL SISTEMA DE PLANTILLAS 121 12. Esta función tiene la responsabilidad de retornar una instancia de Node basada en el contenido de la etiqueta.La etiqueta modo de ejemplo. divide el texto crudo de la plantilla en nodos. esta etiqueta de plantilla es redundante -. Por ejemplo. tag_name. ya que divide en todos los espacios. Escribir etiquetas de plantilla personalizadas Las etiquetas son más complejas que los ltros porque las etiquetas pueden implementar prácticamente cualquier funcionalidad. format_string = token. No escribas el nombre de la etiqueta en el mensaje de error. El Capítulo 4 describe cómo el sistema de plantillas funciona como un proceso de dos etapas: compilación y renderizado. explicaremos todos los pasos necesarios para escribir una etiqueta propia. Escribir la función de compilación Para cada etiqueta de plantilla que encuentra.template. ante cualquier caso de error de sintaxis.aún cuando la etiqueta no lleve argumentos. Por ende.contents es un string con los contenidos crudos de la etiqueta. Para denir una etiqueta de plantilla personalizada.TemplateSyntaxError(msg) return CurrentTimeNode(format_string[1:-1]) Hay muchas cosas en juego aquí: parser es la instancia del parser. Por lo tanto.TemplateSyntaxError con mensajes útiles. Para evaluar esta función. Cuando llamas a render() en una de etiqueta en crudo en un render(). No lo necesitamos en este ejemplo. En las secciones que siguen.4. para denir una etiqueta de plantilla personalizada debes especicar cómo se debe convertir la Node (la función de compilación) y qué hace el método del nodo. con el contexto proporcionado.contents. revisión 789 del 9 de noviembre de 2009 . en nuestro ejemplo sería: 'current_time " %Y.%d %I: %M %p"'.template. Sólo mostramos esta etiqueta a Node: from django import template def do_current_time(parser.split_contents() except ValueError: msg = ' %r tag requires a single argument' % token. Esta función es la responsable de generar la excepción django. El método token.Node y tiene un lista de objetos Node.djangoproject. la plantilla llama a render() render() en cada Node() de su lista de nodos.12. supongamos que la etiqueta deberá <p>The time is { % current_time " %Y. necesitas indicarle a Django cómo manejar ambas etapas cuando llega a tu etiqueta. Evite utilizar token. se deberá obtener el parámetro y crear el objeto { % now %} incluida en Django por defecto hace exactamente lo mismo con una sintaxis más simple.split_contents()[0] raise template.com/r/pyt Es una buena idea denir la sintaxis de la etiqueta previamente.

así como en el segundo ejemplo.strftime(self.122 CAPÍTULO 12. Las funciones de compilación de etiquetas de plantilla deben devolver una subclase de Nodo.tag como un @register.Node): def __init__(self. también es posible utilizar decorador en Python 2. La función de compilación. En este caso. deberás registrar la etiqueta con tu objeto Library dentro del módulo. import datetime class CurrentTimeNode(template. es denir una subclase de Continuando con el ejemplo previo. Sólo deberás instanciar un objeto template. De manera similar a como sucede con el registro de ltros. debemos denir CurrentTimeNode: Node que posea un método render()..tag(name="current_time") def do_current_time(parser... se utilizará el nombre de la función de compilación.tag('current_time'.%m. cualquier La función devuelve el nodo necesita saber sobre esta etiqueta. De esta manera. Si esto se omite. EXTENDER EL SISTEMA DE PLANTILLAS CurrentTimeNode (el cual mostraremos en un momento) conteniendo todo lo que " %Y. Por ejemplo: register.4 o posterior: register.format_string = format_string def render(self. context): now = datetime. Registrar nuevas etique- tas es muy similar a registrar nuevos ltros (como explicamos previamente). token): # . Muchas veces es útil denir variables de plantilla en vez de simplemente devolver valores.datetime.Library y llamar a su método tag(). La función de inicialización sólo necesitará almacenar el string con el formato deseado. Django usará el nombre de la función como nombre Denir una variable en el contexto El ejemplo en la sección anterior simplemente devuelve un valor. @register. do_current_time) El método tag() toma dos argumentos: El nombre de la etiqueta de plantilla (string ).format_string) Estas dos funciones (__init__ y render) se relacionan directamente con los dos pasos para el proceso de la plantilla render() (compilación y renderizado). En el único momento en el cual se le es permitido a las etiquetas de plantilla generar errores es en tiempo de compilación. estas funciones de renderización deberían fallar silenciosamente en lugar de generar errores. sólo pasa el argumento otro valor es un error.tag def shout(parser. Escribir el nodo de plantilla El segundo paso para escribir etiquetas propias. format_string): self. Si omitimos el argumento de la etiqueta. el trabajo real sucede dentro de la función Del mismo modo que los ltros de plantilla.%d %I: %M %p". los autores de plantillas podrán directamente utilizar las variables que esta etiqueta dena. Las comillas son removidas con format_string[1:-1]. Registrar la etiqueta Finalmente. token): # . revisión 789 del 9 de noviembre de 2009 . name.now() return now..

context): now = datetime.contents.now() context['current_time'] = now. 1) except ValueError: msg = ' %r tag requires arguments' % token.TemplateSyntaxError(msg) if not (fmt[0] == fmt[-1] and fmt[0] in ('"'. Una solución más limpia. render() CurrentTimeNode en lugar de devolverla: class CurrentTimeNode2(template. es poder recibir el nombre de la variable en la etiqueta de plantilla así: { % get_current_time " %Y. var_name = m.*?) as (\w+)'.Node): def __init__(self.datetime.var_name = var_name def render(self. ya que CurrentTimeNode2: el nombre de la variable código.format_string = format_string self.groups() else: msg = ' %r tag had invalid arguments' % tag_name raise template. token): # This version uses a regular expression to parse tag contents.search(r'(. context): now = datetime. Entonces. render() siempre debe devolver un string. try: # Splitting by None == splitting by spaces.Node): def __init__(self.format_string) return '' def do_current_time(parser.format_string) return '' Devolvemos un string vacío. EXTENDER EL SISTEMA DE PLANTILLAS 123 Para denir una variable en el contexto.contents[0] raise template.datetime. format_string): self. Aquí mostramos la versión actualizada de current_time.var_name] = now.%M.now() context[self. "'")): revisión 789 del 9 de noviembre de 2009 . si todo lo que la De esta manera usaríamos esta nueva versión de nuestra etiqueta: { % current_time2 " %Y.</p> Pero hay un problema con plantilla. render() debe al menos devolver un string vacío.12.4. var_name): self. format_string. context disponible en el método nuestras variables.</p> Para hacer esto.strftime(self. como si de un diccionario se tratase.TemplateSyntaxError(msg) m = re.%M.%d %I: %M %p" %} <p>The time is {{ current_time }}.%d %I: %M %p" as my_current_time %} <p>The current time is {{ my_current_time }}. Esto signica que tendrás que asegurar que { % current_time %} {{ current_time }} current_time está denido dentro del no sea utilizado en otro lugar dentro de la sobreescribirá el valor de esa otra variable. arg = token.format_string = format_string def render(self. necesitaremos modicar tanto la función de compilación como la clase Node de esta manera: import re class CurrentTimeNode3(template. tag_name.split(None. asignaremos a nuestro objeto que dene una variable de plantilla.strftime(self. debido a que etiqueta hace es denir una variable. arg) if m: fmt.

Node): def render(self. Las etiquetas de plantilla pueden funcionar como bloques que contienen otras etiquetas (piensa en etc. context): output = self. Para crear una etiqueta como esta.delete_first_token() return UpperNode(nodelist) class UpperNode(template.)) parser.parse() toma una tupla django.). en UpperNode. { % comment %} y { % endcomment %}. def do_comment(parser.parse() en tu función estándar { % coment %}: { % if %}. CommentNode.nodelist. la cual Entonces. examina el código fuente para las etiquetas y { % ifchanged %}. context): return '' parser.parse() pero esta vez pasamos el resultado en nodelist a Node: @register. Evaluar hasta otra etiqueta de bloque for %}. nodelist es una lista con todos los nodos entre { % comment %} y { % endcomment %}. { % upper %}. token): nodelist = parser. Por ejemplo. nodelist): self.render() endcomment %} es ignorada. var_name) Ahora. que convertirá a mayúsculas todo hasta la etiqueta { % endupper %}: { % upper %} This will appear in uppercase.NodeList. pero también es posible hacer algo con el código entre estas etiquetas. { % comment %} y {% Evaluar hasta otra etiqueta de bloque y guardar el contenido todo entre En el ejemplo anterior. excluyendo a los mismos Luego de que por eso que en el de nombres de etiquetas de bloque para evaluar y devuelve una instancia de es una lista de todos los objetos Nodo que el parser encontró antes de haber encontrado alguna de las etiquetas nombradas en la tupla.TemplateSyntaxError(msg) return CurrentTimeNode3(fmt[1:-1].)) parser.parse(('endcomment'.delete_first_token() return CommentNode() class CommentNode(template. El mismo simple- ifequal %} Para más ejemplos de renderizado complejo. do_comment() desechó { % comment %} y { % endcomment %}.124 CAPÍTULO 12.delete_first_token() para prevenir que esta simplemente devuelve un string vacío. { % for %}. en el ejemplo previo.py.parse() es llamado el parser aún no ha consumido la etiqueta { % endcomment %}.render(context) Node en la lista de nodos. Puedes encontrarlas en { % if %}.upper() El único concepto nuevo aquí es mente llama a render() en cada self. utilizaremos parser.tag def do_upper(parser. { % django/template/defaulttags.Node): def __init__(self. do_current_time() pasa el string de formato junto al nombre de la variable a CurrentTimeNode3. { % endupper %} Como en el ejemplo previo.parse(('endupper'.nodelist.nodelist = nodelist def render(self. usa Aquí vemos como está implementada la etiqueta parser. presentamos una etiqueta de plantilla. token): nodelist = parser. parser. { % de compilación. es código se necesita llamar explícitamente a parser.template. revisión 789 del 9 de noviembre de 2009 .render(). Luego.render(context) return output. {{ your_name }}. EXTENDER EL SISTEMA DE PLANTILLAS msg = " %r tag's argument should be in quotes" % tag_name raise template. Cualquier cosa entre etiqueta sea procesada nuevamente.

Un par de cosas a tener en cuenta acerca de la función auxiliar Sólo se pasa un argumento a nuestra función.simple_tag(current_time) En Python 2. Para facilitar la creación de esos tipos de etiquetas. y el resto de las piezas necesarias que mencionamos previamente y lo registra con el sistema de plantillas. recibe una función que acepta un argumento. Es probablemente mejor demostrar cómo escribir una usando un ejemplo. pero el destino del enlace cambia dependiendo del objeto que se está modicando.simple_tag def current_time(token): .12. Django provee una función auxiliar: que es un método de función render django.template. Escribamos una etiqueta que produzca una lista de opciones para un simple objeto múltiples opciones.5.4.datetime.all() return {'books': books} Luego creamos la plantilla usada para renderizar la salida de la etiqueta.strftime(format_string) register. Dichos botones siempre se ven igual. Esta función. Por ejemplo la interfaz de administración de Django usa etiquetas de plantillas personalizadas (custom ) para visualizar los botones en la parte inferior de la páginas de formularios agregar/cambiar..4. simple_tag: 12. Etiquetas de inclusión Otro tipo de etiquetas de plantilla común es aquel que visualiza ciertos datos renderizando otra plantilla.4. Siguiendo con nuestro ejemplo.4 la sintaxis de decorador también funciona: @register. de manera que recibimos una cadena común.now(). simple_tag. Las comillas alrededor del argumento (si existieran) ya han sido quitadas.book_set. Ese tipo de etiquetas reciben el nombre de etiquetas de inclusión. Esto será usado como el contexto para el fragmento de plantilla: def show_books_for_author(author): books = author. Un atajo para etiquetas simples Muchas etiquetas de plantilla reciben un único argumento--una cadena o una referencia a una variable de plantilla-y retornan una cadena luego de hacer algún procesamiento basado solamente en el argumento de entrada e información externa. EXTENDER EL SISTEMA DE PLANTILLAS 125 12. la plantilla es muy simple: revisión 789 del 9 de noviembre de 2009 . Por ejemplo la etiqueta current_time que escribimos antes es de este tipo.. lo encapsula en una y retorna la hora como una cadena.Library.4. Nota que nos basta un diccionario y no necesitamos retornar nada más complejo. Se trata de un caso perfecto para el uso de una pequeña plantilla que es llenada con detalles del objeto actual. de manera que no es necesario que lo hagamos nosotros. Usaremos una etiqueta como esta: Poll con { % show_results poll %} El resultado será algo como esto: <ul> <li>First choice</li> <li>Second choice</li> <li>Third choice</li> </ul> Primero denimos la función que toma el argumento y produce un diccionario de datos con los resultados. Le pasamos una cadena de formato. La vericación de la cantidad requerida de argumentos ya ha sido realizada para el momento en el que nuestra función es llamada. Nuestra función current_time podría entonces ser escrita de la siguiente manera: def current_time(format_string): return datetime.

takes_context=True) def jump_link(context): return { 'link': context['home_link'].html') def show_books_for_author(show_books_for_author): .. Para resolver esto Django provee una opción takes_context para las etiquetas de inclusión. si la plantilla se encuentra en un archivo llamado registraremos la plantilla de la siguiente manera: register. o (como veremos) desde un archivo ZIP.126 CAPÍTULO 12. de la siguiente manera: { % jump_link %} 12.html')(show_books_for_author) Como siempre. Continuando con nuestro ejemplo.inclusion_tag('books/books_for_author.5. carga su biblioteca y ejecútala sin argumentos. A veces tus etiquetas de inclusión necesitan tener acceso a valores del contexto de la plantilla padre. Escribir cargadores de plantillas personalizados Los cargadores de plantillas incluidos con Django (descriptos en la sección  Etiquetas de inclusión más arriba) cubrirán usualmente todas tus necesidades de carga de plantillas. la misma no tendrá argumentos obligatorios y la función Python subyacente tendrá un argumento: el contexto de la plantilla en el estado en el que se encontraba cuando la etiqueta fue invocada. Finalmente creamos y registramos la etiqueta de inclusión invocando el método inclusion_tag() sobre un objeto polls/result_snippet. de manera que en cambio podríamos haber escrito: @register. link. 'title': context['home_title']. pero es muy sencillo escribir el tuyo propio si necesitas alguna lógica especial en dicha carga. } Nota El primer parámetro de la función debe llamarse La plantilla context. Si especicas takes_context cuando creas una etiqueta de plantilla. Por ejemplo supongamos que estás escribiendo una etiqueta de inclusión que será siempre usada en un contexto que contiene variables Python: home_link y home_title que apuntan a la página principal. cada vez que desees usar esa etiqueta personalizada. Así es como se vería la función @register.html podría contener lo siguiente: Jump directly to <a href="{{ link }}">{{ title }}</a>.inclusion_tag('link. cada entrada en la variables de conguración objeto invocable (callable ) con la siguiente interfaz: TEMPLATE_LOADERS-- debe ser un load_template_source(template_name. Entonces. la sintaxis de decoradores de Python 2. EXTENDER EL SISTEMA DE PLANTILLAS <ul> { % for book in books %} <li> {{ book }} </li> { % endfor %} </ul> Library. Un cargador de plantillas --esto es.html.inclusion_tag('books/books_for_author. template_dirs=None) revisión 789 del 9 de noviembre de 2009 .. Por ejemplo podrías cargar plantillas desde una base de datos.4 también funciona.html'. o directamente desde un repositorio Subversion usando las librerías (bindings ) Python de Subversion.

y template_path es la ruta desde la cual fue cargada la plantilla. template_name) return (source.get_template() loader.is_usable = True El único paso restante si deseamos usar este cargador es agregarlo a la variable de conguración Si pusiéramos este código en un paquete llamado a TEMPLATE_LOADERS.TemplateDoesNotExist. Usar la referencia de plantillas incorporadas La interfaz de administración de Django incluye una referencia completa de todas las etiquetas y ltros de plantillas disponibles para un sitio determinado.close() # We found a template.zip_loader.zip_loader entonces agregaremos mysite. Está designada para ser una herramienta que los programadores Django proveen a los desarrolladores de plantillas. Si al cargador no le es posible cargar una plantilla. Este es un Booleano que is_usable a le informa a la maquinaria de plantillas si este cargador está disponible en la instalación de Python actual. so return the source. Si un cargador es capaz de cargar en forma exitosa una plantilla. template_dirs=None): """Template loader that loads templates from a ZIP file.load_templ 12. Cada URL en tu sitio tiene allí una entrada separada. Usa una variable de conguración personalizada de búsqueda en lugar de TEMPLATE_ZIP_FILES como una ruta TEMPLATE_DIRS y espera que cada ítem en dicha ruta sea un archivo ZIP contiendo plantillas: import zipfile from django. ltros. Un ejemplo ayudará a claricar todo esto. La página views es la más valiosa. for fname in template_zipfiles: try: z = zipfile. haciendo click en la URL te mostrará lo siguiente: revisión 789 del 9 de noviembre de 2009 .template import TemplateDoesNotExist def load_template_source(template_name. Donde template_source es la cadena de plantilla que será compilada por la maquinaria de plantillas. is_usable.""" template_zipfiles = getattr(settings.ZipFile(fname) source = z. []) # Try each ZIP file in TEMPLATE_ZIP_FILES. mysite. KeyError): continue z. Por ejemplo el cargador desde eggs (que es capaz de cargar plantillas desde eggs Python) ja pkg_resources no se encuentra instalado. template_path) # If we reach here.select_template()) y template_dirs es una lista opcional de directorios en los que se buscará en lugar de TEMPLATE_DIRS. porque pkg_resources False si el módulo es necesario para leer datos desde eggs. las referencias de etiquetas y ltros del Capítulo 4 han sido extraídas directamente de esas páginas) así como cualquier biblioteca de etiquetas o ltros personalizados disponible.read(template_name) except (IOError. "TEMPLATE_ZIP_FILES". Dicha ruta podría ser presentada al usuario para o nes de depuración así que debe identicar en forma rápida desde dónde fue cargada la plantilla. the template couldn't be loaded raise TemplateDoesNotExist(template_name) # This loader is always usable (since zipfile is included with Python) load_template_source.6.template. template_path = " %s: %s" % (fname. modelos y vistas. ve a la interfaz de administración y haz click en el enlace Documentación en la zona superior derecha de la página. debe lanzar Cada función del cargador debe también poseer un atributo de función django.6. La referencia está dividida en cuatro secciones: etiquetas. Para verla.conf import settings from django. USAR LA REFERENCIA DE PLANTILLAS INCORPORADAS 127 El argumento template_name es el nombre de la plantilla a cargar (tal como fue pasado a loader. Aquí tenemos una función cargadora de plantillas que puede cargar plantillas desde un archivo ZIP. TEMPLATE_LOADERS. Las secciones etiquetas y ltros describen todas las etiquetas incluidas (en efecto. debe retornar una tupla: (template_source.12. Si la vista relacionada incluye una docstring. template_path).

12.128 CAPÍTULO 12. En resumen.7. o una lista de variables disponibles en la plantilla de la vista. necesitas importar las partes apropiadas del sistema de plantillas y entonces. El `próximo capítulo`_ describe cómo puedes usar Django para producir imágenes. pero en algunas ocasiones querrás usar Django para generar otros formatos de datos. El nombre de la plantilla o plantillas usados para esa vista.configure() con cualquier valor de conguración TEMPLATE_DIRS (si vas a usar cargadores de plantillas). Todas las variables de congutodos las variables cuyos nombres comienzan con TEMPLATE_ son de obvio utf-8 probablemente sea adecuado) y ración están descriptas en el Apéndice E y 12. Esta no es una suposición incorrecta para un libro sobre desarrollo Web. Congurar el sistema de plantillas en modo autónomo Nota Esta sección es sólo de interés para aquellos que intentan usar el sistema de plantillas como un componente de salida en otra aplicación. Para un ejemplo detallado de la documentación de vistas. revisión 789 del 9 de noviembre de 2009 . Pero si estás usando el sistema de plantillas independientemente del resto de Django. Si estás usando el sistema como parte de un aplicación Django. Normalmente Django carga toda la información de conguración que necesita desde su propio archivo de conguración por omisión. ¾Qué sigue? Hasta ahora este libro ha asumido que el contenido que estás visualizando es HTML. PDFs y cualquier otro formato de datos que puedas imaginar. DEFAULT_CHARSET TEMPLATE_DEBUG. En forma conjunta. Duplicate explicit target name: próximo capítulo. Para resolver este problema necesitas usar la opción de conguración manual descripta en forma completa en el Apéndice E. lee el código fuente de la vista genérica de Django la cual se encuentra en object_list django/views/generic/list_detail. El contexto. el esquema de la variable de entorno no es muy conveniente porque probablemente quieras congurar el sistema de plantillas en una manera acorde con el resto de tu aplicación en lugar de tener que vértelas con archivos de conguración e indicando los mismos con variables de entorno. las páginas de documentación deberían proveerte cada etiqueta. combinado con las variables de conguración en el módulo indicado en la variable de entorno DJANGO_SETTINGS_MODULE. Debido a que los sitios implementados con Django generalmente usan objetos de bases de datos. antes de invocar alguna de las funciones de plantillas. invoca que desees especicar.conf. EXTENDER EL SISTEMA DE PLANTILLAS El nombre de la función de vista que genera esa vista. las páginas models describen cada tipo de objeto en el sistema así como todos los campos disponibles en esos objetos. ltro.py. django.8. variable y objeto disponible para su uso en una plantilla arbitraria. la información aquí presentada no es relevante para ti.settings. Una breve descripción de qué hace la vista. Podrías desear considerar jar al menos (aunque el valor por omisión interés.

Como un ejemplo de cómo funciona esto. la usamos para distribuir datos en todo tipo de formatos: RSS. HttpResponse.. veamos una vista que devuelve una imagen PNG.http import HttpResponse def my_image(request): image_data = open("/path/to/my/image. simplemente leeremos un chero desde el disco: from django.png". PDFs. open() con la ruta a una imagen real.Capítulo 13 Generación de contenido no HTML Usualmente cuando hablamos sobre desarrollo de sitios Web.read() return HttpResponse(image_data. imágenes. hablamos de producir HTML. Django posee varias herramientas útiles que puedes usar para producir algunos tipos comunes de contenido no HTML: Feeds de sindicación RSS/Atom Mapas de sitios haciendo uso de Sitemaps (un formato XML originalmente desarrollado por Google que provee de ayuda a motores de búsqueda) Examinaremos cada una de esas herramientas un poco más adelante. una redirección. especícamente en el mimetype del constructor. es simplemente una función en Python que recibe una petición Web y retorna una respuesta Web. hay mucho más que contenido HTML en la Web. y así sucesivamente. Esta respuesta puede ser el contenido HTML de una página Web. para cheros. podemos indicarle al navegador que hemos retornado Por ejemplo. Más formalmente. o una vista por abreviar. La clave para retornar contenido no HTML desde una vista reside en la clase argumento una respuesta en un formato diferente. Cambiando el tipo MIME. una función vista Django debe Aceptar una instancia HttpRequest como primer argumento. Lo básico: Vistas y tipos MIME ¾Recuerdas esto del Capítulo 3? Una función vista. mimetype="image/png") ½Eso es todo! Si sustituimos la ruta de la imagen en la llamada a La otra cosa importante a tener presente es que los objetos biblioteca de terceros) espera un chero. Hasta ahora nos hemos concentrado en el caso común de la producción de HTML. pero en ese capítulo tomaremos un desvío y veremos cómo usar Django para producir otro tipo de contenido. Retornar una instancia HttpResponse. podemos usar esta vista bastante sencilla para servir una imagen. Esto signica que podemos usar una instancia de HttpResponse implementan el API estándar de Python HttpResponse en cualquier lugar donde Python (o revisión 789 del 9 de noviembre de 2009 . una imagen. veamos la producción de CSV con Django. "rb"). un error 404. Para mantener las cosas sencillas. cualquier cosa.1. 13. y el navegador la mostrará correctamente. en realidad. Por supuesto.. pero antes cubriremos los principios básicos. un documento XML.

E..251.235.184.226 2000. Afortunadamente.U. pero hay unas pocas cosas que merecen mención especial: Se le da a la respuesta el tipo MIME text/csv (en lugar del tipo predeterminado text/html). En una aplicación real # esto vendría desde una base de datos o cualquier otro medio de almacenamiento. la parte adjunta) le indicará al navegador que solicite la ubicación donde guardará el chero (en lugar de simplemente mostrarlo).184 1997. es muy fácil usar un HttpResponse en lugar import csv from django. 2006).203] def unruly_passengers_csv(request): # Creamos el objeto Httpresponse con la cabecera CSV apropiada.304. num]) return response El código y los comentarios deberían ser bastante claros.writerow([year. de cortesía E. Esto le dice a los navegadores que el documento es un chero CSV. llámalo como quieras.235 1998. filename=unruly.faa.273 2003.281 2004.203 Nota El listado precedente Federal contiene de números reales. haciendo un poco complicado usarlo. de la Vea Administración Aviación (FAA) http://www.200 1999. Producción de CSV CSV es un formato de datos sencillo que suele ser usada por programas de hojas de cálculo.writer(response) writer.281. no es un formato que ha sido denido formalmente. num) in zip(range(1995. 'Unruly Airline Passengers']) for (year.gov/data_statistics/passengers_cargo/unruly_passengers/.226. Aunque CSV parezca simple.251 2001. La respuesta obtiene una cabecera Content-Disposition adicional.299.Unruly Airline Passengers 1995. Diferentes programas producen y consumen diferentes variantes de CSV.130 CAPÍTULO 13. que es bastante robusta. la cual contiene el nombre del chero CSV.U.299 2002.. Esta cabecera (bueno. UNRULY_PASSENGERS = [146.200. El nombre de chero es arbitrario.146 1996.304 2005. Básicamente es una serie de las en una tabla.writerow(['Year'.csv' # Creamos un escritor CSV usando a HttpResponse como "fichero" writer = csv. Será usado por los navegadores en el cuadro de diálogo Guardar como. csv opera sobre objetos similares a cheros.2. revisión 789 del 9 de noviembre de 2009 . cada celda en la la está separada por comas (CSV signica comma-separated values ).2005. response = HttpResponse(mimetype='text/csv') response['Content-Disposition'] = 'attachment.http import HttpResponse # Número de pasajeros problematicos por año entre 1995 . UNRULY_PASSENGERS): writer.273. aquí tienes una lista de pasajeros problemáticos en líneas aéreas en formato CSV: Year. Python incluye una biblioteca estándar para CSV. Debido a que el módulo de un chero: csv. Por ejemplo. GENERACIÓN DE CONTENIDO NO HTML 13.

la generación de PDFs en forma dinámica con Django es sencilla porque la API ReportLab actúa sobre objetos similares a cheros (le-like según la jerga Python). Limítate a pasar la información a writerow().pdf' revisión 789 del 9 de noviembre de 2009 .org/rl_toolkit. que hará lo correcto. Instalar ReportLab Antes de que puedas generar ningún PDF. HttpResponse de respuesta (con un tipo MIME especial). invocamos a una lista o una tupla. http://www. Este es el patrón general que usarás siempre que necesites retornar contenido no HTML: crear un objeto Veamos unos cuántos ejemplos más.2. pdf http://www.org/downloads.3. Generar PDFs El Formato Portable de Documentos (PDF.supongamos. efectivamente. si estás usando la (excelente) distribución Ubuntu.html. para diferentes usuarios u diferentes contenidos.http import HttpResponse def hello_pdf(request): # Create the HttpResponse object with the appropriate PDF headers. por Portable Document Format) es un formato desarrollado por Adobe que es usado para representar documentos imprimibles. Puedes pensar en un documento PDF como el equivalente digital de un documento impreso.3.com para generar programas de torneos de la NCAA personalizados. ajustan. Puedes generar PDFs fácilmente con Python y Django gracias a la excelente biblioteca open source ReportLab (http://www. La ventaja de generar cheros PDFs dinámicamente es que puedes crear PDFs a medida para diferentes propósitos -.org/rsrc/userguide.pdfgen import canvas from django.writerow. Usar el API de generación de CSV es sencillo: basta pasar La función csv. 13.3. Nota Si estás usando una distribución moderna de Linux. podrías desear comprobar con la utilidad de manejo de paquetes de software antes de instalar ReportLab. pasárselo a algo que espera un chero.1. Por ejemplo. response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment. y los de tipo HttpResponse se Por cada la en el chero CSV. A continuación un ejemplo Hola Mundo: from reportlab. La mayoría de los repositorios de paquetes ya incluyen ReportLab.reportlab.13. 13.3. un simple python-reportlab apt-get install hará la magia necesaria.writer response como primer argumento a espera un objeto de tipo chero. Escribir tu Vista Del mismo modo que CSV. writer. así que no tendrás que preocuparte por escapar caracteres en las cadenas que tengan comillas o comas en su interior. los PDFs se usan normalmente cuando se necesita entregar un documento a alguien para que lo imprima. deberás instalar ReportLab.reportlab. Por ejemplo. completos con formato perfecto hasta un nivel de detalle medido en pixels. Prueba tu instalación importando la misma en el intérprete interactivo Python: >>> import reportlab Si ese comando no lanza ningún error.writer. tipografías empotradas y grácos de vectores en 2D. pasándole un objeto iterable como El módulo CSV se encarga de poner comillas por ti. 13. Esto es usualmente muy simple: sólo descarga e instala la biblioteca desde La guía del usuario (naturalmente sólo disponible en formato PDF) en contiene instrucciones de instalación adicionales. listos para ser impresos. GENERAR PDFS 131 csv. y luego devolver la respuesta. la instalación funcionó.html). hemos usado Django y ReportLab en KUSports. filename=hello.reportlab.

Finalmente. Esto le indica al navegador que el documento es un chero PDF y no un chero HTML.drawString(100. a la norma. response = HttpResponse(mimetype='application/pdf') response['Content-Disposition'] = 'attachment. y los objetos HttpResponse se ajustarán Todos los métodos de generación de PDF subsecuentes son llamados pasándoles el objeto PDF (en este caso p). La clase Canvas response como el primer argumento a espera un objeto le-like. and we're done.showPage() p. 100. p. p. showPage() y save() del objeto PDF (de otra manera 13.Canvas.pdf' temp = StringIO() # Create the PDF object. es importante llamar a los métodos obtendrás un chero PDF corrupto).") # Close the PDF object cleanly." p = canvas.getvalue()) return response revisión 789 del 9 de noviembre de 2009 . "Hello world. La biblioteca cStringIO provee una interfaz vía objetos le-like que está escrita en C para máxima eciencia.") # Close the PDF object cleanly. Si no incluyes esta información. using the response object as its "file. # See the ReportLab documentation for the full list of functionality. los navegadores web probablemente interpretarán la respuesta como HTML. response. considera usar la cStringIO como un lugar de almacenamiento temporario para tu chero PDF. GENERACIÓN DE CONTENIDO NO HTML # Create the PDF object.showPage() p. Here's where the PDF generation happens. # See the ReportLab documentation for the full list of functionality. Ese es el ejemplo Hola Mundo anterior modicado para usar cStringIO: from cStringIO import StringIO from reportlab. p. Here's where the PDF generation happens. Interactuar con la API ReportLab es sencillo: sólo pasa canvas. p.3.save() return response Son necesarias alguna notas: Usamos el tipo MIME application/pdf.write(temp.132 CAPÍTULO 13.pdfgen import canvas from django. "Hello world. biblioteca PDFs complejos Si estás creando un documento PDF complejo (o cualquier pieza de datos de gran tamaño).save() # Get the value of the StringIO buffer and write it to the response.Canvas(temp) # Draw things on the PDF. using the StringIO object as its "file.http import HttpResponse def hello_pdf(request): # Create the HttpResponse object with the appropriate PDF headers. 100.drawString(100. lo que resultará en jeroglícos en la ventana del navegador.Canvas(response) # Draw things on the PDF. no response. filename=hello." p = canvas.3.

GIF. http://www.13. Esta línea de URLconf tiene un argumento extra: Especícamente. Ploteos y Grácos : Existe un número importante de increíblemente potentes bibliotecas de Python para Ploteo y Grácos.gov/wiki/pygraphviz). {'feed_dict': feeds} ).lanl. {'feed_dict': feeds}. Imágenes Dinámicas : Biblioteca Python de procesamiento de Imágenes (Python Imaging Library.4. Django incluye algunas herramientas bonitas e ingeniosas para generar cierto tipo de contenido no-HTML.com/products/pil/) es una herramienta fantástica para producir imágenes (PNG. PIL.contrib. dibujos. Inicialización Para activar los feeds de sindicación en tu sitio Django. y muchas más). JPEG. agrega lo siguiente en tu URLconf: (r'^feeds/(?P<url>. Es imposible listar todas las bibliotecas. Este es slug (etiqueta corta de URL) de un feed a la un ejemplo completo de URLconf: revisión 789 del 9 de noviembre de 2009 .*)/$'. Ahora que hemos visto lo básico de generar contenido no-HTML. http://www. Puedes denir el feed_dict debe ser un diccionario que mapee el feed_dict en el mismo URLconf. que se pueden utilizar para generar mapas.whatisrss. cualquier biblioteca Python capaz de escribir en un chero puede ser utilizada dentro de Django. y Para crear cualquier feed de sindicación. Aquí tenemos algunas otras ideas y las bibliotecas que podrías usar para implementarlas: Archivos ZIP : La biblioteca estándar de Python contiene el módulo zipfile. (Puedes por algo que se adapte a tus necesidades). Puedes usarla para guardar cheros bajo demanda. Esa línea le indica a Django que use el framework RSS para captar las URLs que comienzan con cambiar "feeds/" "feeds/". el nal de la URL (todo lo que este después de 3 y 8 para más información sobre URLconfs). Lee más sobre RSS en obtén información sobre Atom en http://www. Usa este argumento extra para pasar al framework de feeds de sindicación los feeds que deben ser publicados en dicha URL.org/. OTRAS POSIBILIDADES 133 13.4. Para crear un feed. ploteos y grácos.atomenabled. El Framework de Feeds de Sindicación Django incluye un framework para la generación y sindicación de feeds de alto nivel que permite crear feeds RSS y Atom de manera sencilla. una interfaz con la herramienta Graphviz (http://graphviz. Las posibilidades son realmente interminables. Otras posibilidades Hay innidad de otros tipos de contenido que puedes generar en Python.5.pythonware. puede usarse para generar diagramas estructurados de grafos y redes. que puede escribir y leer cheros comprimidos en formato ZIP.5.views.org/). En general. necesitas escribir una clase Feed y hacer referencia a la misma en tu URLconf (ver los Capítulos El framework de generación de feeds de alto nivel es una vista enganchada a 13. De la misma manera puedes generar cheros en formato TAR usando el módulo de la biblioteca estándar tarfile.feed'.syndication. • pygraphviz (https://networkx.1.com/. ¾Qué es RSS? ¾Qué es Atom? RSS y Atom son formatos basados en XML que se puede utilizar para actualizar automáticamente los feeds con el contenido de tu sitio. o quizás comprimir grandes documentos cuando lo requieran.sourceforge. avancemos al siguiente nivel de abstracción.net/) puede usarse para generar plo- teos de alta calidad al estilo de los generados con MatLab o Mathematica. agrupar varias imágenes en un solo marco e incluso realizar procesamiento de imágenes directamente en la web. todo lo que debes hacer es escribir una corta clase Python. /feeds/ por convención. Django usa /feeds/) para determinar qué feed retornar. Puedes crear tantos feeds como desees. 13. así que resaltamos algunas de ellas: • matplotlib (http://matplotlib. 'django. clase Feed. Puedes usarla para escalar automáticamente imágenes para generar miniaturas.

syndication. pasándole dos variables de contexto para plantillas: revisión 789 del 9 de noviembre de 2009 . y description corresponden a los elementos RSS estándar <title>. y <description> La clase es subclase de respectivamente. agregados: Un Feed simple Este ejemplo simple. <link>. Un feed puede ser simple (p. tomado de http://chicagocrime. 'categories': LatestEntriesByCategory. LatestEntriesByCategory residirá en feeds/categories/. ej.views. o una lista de las últimas entradas del blog) o más complejo (p. } urlpatterns = patterns(''.conf.. (r'^feeds/(?P<url>.syndication.feed'. # .org.contrib.org site news" link = "/sitenews/" description = "Updates on changes and additions to chicagocrime.defaults import * from myproject. title. noticias del sitio. Una vez que este congurado. link. Obtienes unos pocos bits de funcionalidad gratis usando los modelos de Django.org.*)/$'.5.feeds import Feed from chicagocrime. Aunque este ejemplo retorna objetos datos de Django. donde latest es el slug especicado en URLconf para el feed dado. En un feed RSS. Esta puede residir en cualquier 13.order_by('-pub_date')[:5] Las cosas importantes a tener en cuenta son: django. pero retornar cualquier tipo de objeto que desees. crea plantillas Django (ver Capítulo 4) feeds/latest_title." def items(self): return NewsItem. mostrar todas las entradas de un blog en una categoría en particular. # . <link>.urls. y <description>. {'feed_dict': feeds}). GENERACIÓN DE CONTENIDO NO HTML from django.2.syndication... 'django. no es un requerimiento que items() NewsItem usando la API de base de deba retornar instancias de modelos. Hay solamente un paso más.Feed. items() es simplemente un método que retorna una lista de objetos que deben incluirse en el feed como elementos <item>.feeds. cada items() puede <item> posee <title>. La clase parte del árbol de código. Necesitamos decirle al framework qué datos debe poner en cada uno de los elementos. donde la categoría es variable).Feed.feeds import LatestEntries. El sistema RSS renderiza dicha plantilla por cada ítem.html.html y feeds/latest_description. Para especicar el contenido de llamadas <title> y <description>.html es requerida. ) El ejemplo anterior registra dos feeds: El feed representado por El feed representado por LatestEntries residirá en feeds/latest/.objects.. Feed. Feed debe ser una subclase de django. describe un feed que muestra los últimos cinco items from django.feeds. LatestEntriesByCategory feeds = { 'latest': LatestEntries.134 CAPÍTULO 13.models import NewsItem class LatestEntries(Feed): title = "Chicagocrime. necesitas denir la propia clase Una clase Feed es una simple clase Python que representa un feed de sindicación.contrib.contrib. Notar que la extensión . ej.syndication.contrib.

Django primero tratará de ejecutar el método get_absolute_url() en dicho objeto. hay dos opciones.3. Para el ejemplo anterior contiene: y {{ obj. latest_title.chicagocrime..core.org: Crimes for beat %s" % obj.html {{ obj.org/rss/beats/0613/: partamento 0613 Retorna los crímenes más recientes para el de- http://www. el framework utilizará la plantilla por omisión "{{ obj }}" -. los feed por departamento de policía son accesibles mediante URLs como estas: http://www. obj): return obj.chicagocrime. En chicagocrime.org ofrece un feed RSS de los crímenes recientes de cada departamento de policía en Chicago.sites. Un Feed más complejo El framework también permite la creación de feeds más complejos mediante el uso de parámetros. por Do not repeat yourself ) y crearía acoplamiento entre los datos y la lógica de En su lugar.html de Python.domain }} o {{ site. podemos usar plantillas de feed muy simples. EL FRAMEWORK DE FEEDS DE SINDICACIÓN 135 • obj: El objeto actual (uno de los tantos que retorna en items()). obj): return "Chicagocrime.13. pasándole un único parámetro. http://chicagocrime.5. Para especicar el contenido de <link>.get(beat__exact=bits[0]) def title(self. la cadena normal de representación del objeto. 13. Ambos get_absolute_url() y item_link() deben retornar la URL del ítem como una cadena normal LatestEntries. Este es el código para los feeds por departamento: from django. Un ejemplo aclarará esto.description }} contiene: Es casi demasiado fácil.core. el framework de feeds de sindicación te permite crear feeds genéricos que retornan items basados en la información en la URL del feed.title }} latest_description.org/rss/beats/1424/: partamento 1424 El slug aquí es Retorna los crímenes más recientes para el de- "beats".Site representa el sitio actual. or other such # clutter.. Por cada ítem en items(). como atributos de tu clase title_template y description_template Feed. Esto es útil para {{ site. bits): # In case of "/rss/beats/0613/foo/bar/baz/". También puedes cambiar los nombres de estas plantillas especicando que es el objeto en sí mismo. item. Si no creas una plantilla para el título o la descripción.models. if len(bits) != 1: raise ObjectDoesNotExist return Beat.objects. Por ejemplo.name }}. esto puede violar el principio No te repitas a ti mismo (DRY.get_absolute_url() revisión 789 del 9 de noviembre de 2009 . Feed separada por cada departamento.exacto.5. • site: Un objeto django.org. El framework de sindicación ve las partes extra en la URL tras el slug -- 0613 y 1424 -- y te provee un gancho (hook ) para que le indiques qué signica cada uno de esas partes y cómo inuyen en los items que serán publicados en el feed.exceptions import ObjectDoesNotExist class BeatFeed(Feed): def get_object(self. check that bits has only one member. entonces trata de llamar al método item_link() en la clase Feed.beat def link(self. Si dicho método no existe. Sería tonto crear una clase programación.

utils. Para cambiar eso. se utilizaron atributos simples de clase string. En el y <description> del feed.4.id) return crimes. el framework de feeds de sindicación produce RSS 2. Django sigue este algoritmo: 1. feed_type una clase. Esa función. 3. pero este ejemplo muestra que estos pueden ser strings o métodos. get_object() get_object() es el responsable de obtener el departamento requerido. ['0613'. asumiendo esa clase y un requerimiento a la URL 1. bits dado.core. Para generar los campos title(). y Beat.91 Atom 1.0 django. los trozos son serán 2.0. description(). obj. Por cada description.exceptions. Feed siempre esta disponible en la La documentación completa de todos los métodos y atributos de las clases documentación ocial de Django (http://www.com/documentation/0.beat def items(self.utils.Rss201rev2Feed django.feedgenerator.ObjectDoesNotExist si try/except abarcando la llamada a Beat.get() porque no es necesario.Atom1Feed revisión 789 del 9 de noviembre de 2009 . donde obj es el objeto 2.utils. Trata de llamar al método. Finalmente. Si eso falla. Si eso falla. /rss/beats/0613/foo/bar/.objects.order_by('-crime_date')[:30] Aquí tenemos el algoritmo básico del framework RSS. 13. ['0613']. 'foo'. a tu clase Especicar el tipo de Feed Por omisión. Django usa los métodos ejemplo anterior. link. agrega un atributo Feed: feed_type from django. 4. pasando el argumento retornado por get_object(). 'bar']. GENERACIÓN DE CONTENIDO NO HTML def description(self.DoesNotExist.primero prueba items(obj).136 CAPÍTULO 13.feedgenerator import Atom1Feed class MyFeed(Feed): feed_type = Atom1Feed Nota que asignas como valor de se muestran en la Tabla 11-1. Lanzar la excepción ObjectDoesNotExist en get_object() le dice a Django que produzca un error 404 para el requerimiento en curso. no una instancia.01 (por defecto) RSS 0. usa la API de base de datos de Django para obtener el departamento.feedgenerator. ante una falla lanza la excepción Beat. obj): crimes = Crime.djangoproject.utils.RssUserland091Feed django. trata de llamar al método sin argumentos.DoesNotExist es una subclase de ObjectDoesNotExist. y y <title>. a partir del En este caso. link(). y nalmente un atributo de clase items (que debe ser una lista). obj): return "Crimes recently reported in police beat %s" % obj. El algoritmo items es el mismo que se describe en el paso anterior -. Los tipos de feeds disponibles actualmente Cuadro 13. nota que para items() en el ejemplo también toma como argumento a obj.feedgenerator. 3. Notar que debe capturar la excepción recibe parámetros inválidos.objects. después items().5. No hay django. usa los atributos de clase.1: Tipos de Feeds Clase Feed Formato RSS 2. El framework toma la URL clase /rss/beats/0613/: el slug. title.96/syndication_feeds/). Separa esa cadena remanente por el carácter Feed /rss/beats/0613/ y nota que la URL contiene una parte extra tras "/" y llama al método get_object() de la Para un requerimiento a pasándole los trozos (bits ) resultantes.filter(beat__id__exact=obj. <link>. En este caso.

Aquí un ejemplo completo: from django. Publicar feeds Atom y RSS conjuntamente Algunos desarrolladores preeren ofrecer ambas versiones Atom y RSS de sus feeds.models import Song class MyFeedWithEnclosures(Feed): title = "Example feed with enclosures" link = "/feeds/example-with-enclosures/" def items(self): return Song.7.6. ej. Los feeds Atom requieren un <link rel="self"> que dene la ubicación actual del feed. usa los ganchos item_enclosure_url. item): return item.contrib.5. Si link no retorna el dominio.org. item_enclosure_length. recursos multimedia asociados al ítem del feed tales como feeds de podcasts MP3). ej.all()[:30] def item_enclosure_url(self.feeds import Feed from chicagocrime.com/blog/").utils. ej. Song con los campos song_url y song_length (p.0) LANGUAGE_CODE. "/blog/") como una URL con el nom"http://www.8.5. bre completo de dominio y protocolo (p.5. Esto es simple de hacer con Django: solamente crea una subclase de tu clase feed y asigna a feed_type un valor diferente. EL FRAMEWORK DE FEEDS DE SINDICACIÓN 137 13. usando el dominio del sitio actual acorde a la variable de conguración SITE_ID.org site news" link = "/sitenews/" description = "Updates on changes and additions to chicagocrime.models import NewsItem from django. item): return item. por ejemplo: from myproject. El método/atributo puede retornar tanto una URL absoluta (p." def items(self): return NewsItem. el atributo Idioma Los Feeds creados por el framework de sindicación incluyen automáticamente la etiqueta xml:lang URLs apropiados (Atom). y item_enclosure_mime_type.objects. que has creado un objeto tamaño en bytes). el framework de sindicación insertará el dominio del sitio actual. Luego actualiza tu URLconf para agregar una versión extra.syndication.example. Esto viene directamente de tu variable de conguración <language> (RSS 2.13.objects.order_by('-pub_date')[:5] class AtomSiteNewsFeed(RssSiteNewsFeed): feed_type = Atom1Feed Y este es el URLconf asociado: revisión 789 del 9 de noviembre de 2009 . El framework de sindicación completa esto automáticamente.feedgenerator import Atom1Feed class RssSiteNewsFeed(Feed): title = "Chicagocrime.song_length item_enclosure_mime_type = "audio/mpeg" Esto asume.5.5. link 13. Enclosures Para especicar enclosures (p. el 13.song_url def item_enclosure_length(self. ej. acorde a la variable de conguración SITE_ID. o 13. por supuesto.5.

.org/schemas/sitemap/0. por lo que los cambios son necesarios solamente si modicaste dicha variable de conguración.1</priority> </url> . vea http://www.*)/$'.sitemaps.contrib.com/documentation/0_90/</loc> <changefreq>never</changefreq> <priority>0.sitemaps' a tu variable de conguración INSTALLED_APPS.conf. # .. revisión 789 del 9 de noviembre de 2009 . Asegúrate de que tá en tu variable de conguración 'django.urls. La única razón de que esté en INSTALLED_APPS es que el cargador de plantillas load_template_source pueda encontrar las plantillas incluídas. 13.feed'.djangoproject.9"> <url> <loc>http://www. 'django.contrib. debes simplemente escribir una clase URLconf.com/sitemap. 2. Por ejemplo.views. GENERACIÓN DE CONTENIDO NO HTML from django.6. Agrega 'django.syndication. Asegúrate de que tienes instalado el framework sites (ver Capítulo 14).loaders.138 CAPÍTULO 13. esta es una parte del sitemap del sitio web de Django (http://www. El framework Sitemap Un sitemap es un chero XML en tu sitio web que le indica a los indexadores de los motores de búsqueda cuan frecuentemente cambian tus páginas así como la importancia relativa de ciertas páginas en relación con otras (siempre hablando de páginas de tu sitio).template.djangoproject. sigue los siguientes pasos: 1.sitemaps.. Por omisión se encuentra activado. Para crear un sitemap.com/documentation/</loc> <changefreq>weekly</changefreq> <priority>0.1. xml): <?xml version="1. Sitemap y hacer referencia a la misma en tu El framework sitemap de Django automatiza la creación de este chero XML si lo indicas expresamente en el código Python.org/.djangoproject. AtomSiteNewsFeed feeds = { 'rss': RssSiteNewsFeed.feeds import RssSiteNewsFeed. Nota La aplicación sitemap no instala tablas en la base de datos. # ..6..0" encoding="UTF-8"?> <urlset xmlns="http://www. Esta información ayuda a los motores de búsqueda a indexar tu sitio. {'feed_dict': feeds}). } urlpatterns = patterns(''.defaults import * from myproject.load_template_source' esTEMPLATE_LOADERS.. </urlset> Para más información sobre sitemaps. 'atom': AtomSiteNewsFeed. ) 13. 3. Instalación Para instalar la aplicación sitemap.5</priority> </url> <url> <loc>http://www.app_directories. (r'^feeds/(?P<url>.

si tu directorio principal.13. el mismo puede hacer referencia a cualquier URL en tu sitio. 'django.pub_date Declarar un Sitemap debería verse muy similar a declarar un En manera similar a las clases Una clase Feed.3. Los motores de búsqueda solamente indexan los enlaces en tu sitemap para el nivel de URL actual y anterior. blog o news) a tu clase Sitemap (p. asumamos que posees un sistema de blog. y otra sitemap. Por puede representar todos los eventos de tu calendario.contrib. el framework llamará al método get_absolute_url() en cada uno de los items().xml reside en El nombre del chero sitemap no es importante. obj): return obj. todo location().com/foo/bar/' objetos retornados por location no es provisto. ej. Las clases parte del árbol de código. location (opcional): Provee la URL absoluta para el objeto dado.xml$'. Ver los pasos en la sección  Un feed más complejo para más información sobre cómo funciona esto. En el caso más simple. lastmod(). Al framework no le importa que tipo de objeto es. Tu clase from django.. uno por sección Sitemap debe ser una subclase de django. changefreq especicaciones de Sitemaps) son: revisión 789 del 9 de noviembre de 2009 .xml. EL FRAMEWORK SITEMAP 139 13.com/foo/bar/' 'http://example. Aquí URL absoluta URL que no incluye el protocolo o el dominio.contrib.sitemaps.6. con un modelo todos los enlaces a las entradas individuales de tu Blog. pero también es posible usar el framework para generar un índice sitemap que haga a referencia cheros sitemap individuales. {'sitemaps': sitemaps}. Pero si tu sitemap reside en /content/sitemap.sitemap'. agrega la siguiente línea a tu URLconf: (r'^sitemap.blog. todas estas secciones se unen en un único (describiéndolo sintéticamente). changefreq().xml. Los valores posibles (según indican las lastmod (opcional): La fecha de última modicación del objeto. los miembros de Sitemap Feed. y priority().5 def items(self): return Entry. instancia de una clase Sitemap (p. Estas pueden residir en cualquier Sitemap Entry.models import Entry class BlogSitemap(Sitemap): changefreq = "never" priority = 0. Clases Sitemap Una clase ejemplo.sitemaps.Sitemap.6. una clase Sitemap es simplemente una clase Python que representa una sección de Sitemap puede representar todas las entradas de tu weblog. esto es justamente un objetivo del diseño.sitemaps import Sitemap from mysite. y quieres que tu sitemap incluya debería verse así: Por ejemplo.xml. Sitemap puede denir los siguientes métodos/atributos: lo que importa es que los objetos sean pasados a los métodos items (requerido): Provee una lista de objetos. entradas en tu sitemap. Por ejemplo. solamente podrá hacer referencia a URLs que comiencen con La vista sitemap toma un argumento extra: mapee una etiqueta corta También mapea hacia una /content/. sitemap.filter(is_draft=False) def lastmod(self. BlogSitemap o NewsSitemap). como un objeto datetime de Python.2. ej. Estos son algunos ejemplos: signica una • • • Si Bien: Mal: Mal: '/foo/bar/' 'example. (opcional): Cuán a menudo el objeto cambia.contrib. pero la ubicación sí lo es. Inicialización Para activar la generación del sitemap en tu sitio Django.e. 13. {'sitemaps': sitemaps}) Esta línea le dice a Django que construya un sitemap cuando un cliente accede a /sitemap. sitemaps debe ser un diccionario que de sección (p.6. pueden ser métodos o atributos. BlogSitemap(some_var)).views.objects.

6. También debe poseer una entrada date_field que especica un campo fecha para los objetos obtenidos del queryset.views. FlatPageSitemap como GenericSiteMap (con el anterior objeto hipotético Entry): La clase Para usarla.sitemaps..6). info_dict que se pasa a la vista genérica. Esto será usado por el atributo lastmod en el sitemap generado. 'blog': GenericSitemap(info_dict.5.0 y 1. pasándola en el mismo from django.xml$'. {'sitemaps': sitemaps}) ) revisión 789 del 9 de noviembre de 2009 . crea una instancia. 'date_field': 'pub_date'.all().contrib.no lastmod.sitemaps. Accesos directos El framework sitemap provee un conjunto de clases para los casos más comunes.sitemaps import FlatPageSitemap.0.objects. ver priority. También puedes pasar los argumentos palabra clave (keyword ) priority y changefreq al constructor GenericSitemap para especicar dichos atributos para todas las URLs.models import Entry info_dict = { 'queryset': Entry. location -. Estas entradas incluyen solamente el atributo changefreq.blog. GenericSitemap from mysite.contrib.140 CAPÍTULO 13. FlatPageSitemap La clase django.contrib. priority. # some generic view using info_dict # . } urlpatterns = patterns(''.FlatPageSitemap o apunta a todas las páginas planas denidas para el sitio actual y crea una entrada en el sitemap. Sitemap Genérico GenericSitemap trabaja con cualquier vista genérica (ver Capítulo 9) que pudieras poseer con anterioridad. Para más información sobre Páginas Planas ver el Capítulo 14. # the sitemap (r'^sitemap. El único requerimiento es que el diccionario tenga una entrada queryset. GENERACIÓN DE CONTENIDO NO HTML • • • • • • • 'always' 'hourly' 'daily' 'weekly' 'monthly' 'yearly' 'never' 0. 'django. Este es un ejemplo de URLconf usando tanto.defaults import * from django.4. priority=0.org para más información de cómo 13. (opcional): Prioridad sugerida de indexado entre la documentación de priority funciona una página es 0. Describiremos estos casos en las secciones a continuación.urls.conf.sitemap'. La prioridad por omisión de http://sitemaps. } sitemaps = { 'flatpages': FlatPageSitemap..

llamado django.6.sitemaps. Crear un índice Sitemap El framework sitemap también tiene la habilidad de crear índices sitemap que hagan referencia a cheros sitemap individuales. pass Una solución más eciente.ping_google(). Las única diferencias de uso son: django.contrib. El framework provee una función para hacer justamente eso: django. Nota Hasta el momento en que este libro se escribió. {'sitemaps': sitemaps}) y sitemap-blog. así que asegúrate de vericar la ultima documentación http://www. sería llamar a ping_google() save(). Una forma útil de llamar a ping_google() save(): from django.save() try: ping_google() except Exception: # Bare 'except' because we could get a variety # of HTTP-related exceptions.. Así deberían verse las líneas relevantes en tu URLconf para el ejemplo anterior: (r'^sitemap. que debe ser la URL absoluta de tu sitemap (por ej. sitemap_url.SitemapNotFound es desde el método si no puede determinar la URL de tu sitemap.views.com/documentation/0.sitemap'.sitemaps import ping_google class Entry(models. La función hace un pedido HTTP a los servidores de Google.views. EL FRAMEWORK SITEMAP 141 13.contrib.index y django.sitemaps.xml$'.djangoproject. {'sitemaps': sitemaps}). únicamente Google responde a los pings de sitemap. ping_google() tratará de generar un sitemap realizando una búsqueda reversa en tu URLconf.contrib. Hacer ping a Google Puedes desear hacer un ping a Google cuando tu sitemap cambia.. Si este argumento no es provisto.sitemap section.xml que hace referencia a ambos cheros sitemap-flatpages.+). uno por cada sección denida en tu diccionario Usas dos vistas en tu URLconf: La vista sitemaps. desde un script cron o un manejador de tareas.views.96/sitemaps/. cambiaremos el nombre de ping_google() a ping_search_engines(). 'django. eso suceda.Model): # .sitemaps.xml'). sin embargo.5. 13.xml sitemaps no cambian en absoluto.contrib.contrib. Cuando algo como de sitemap en ping_google() toma un argumento opcional.6. self). def save(self): super(Entry.sitemaps. por lo que no querrás introducir esa demora asociada a la actividad de red cada vez que se llame al método revisión 789 del 9 de noviembre de 2009 .views. '/sitemap.6.6. (r'^sitemap-(?P<section>. Esto genera automáticamente un chero La clase Sitemap y el diccionario sitemap. ping_google() lanza la excepción django.sitemaps.views.xml..sitemaps. 'django.index'.contrib.sitemaps.13. para hacerle saber que debe reindexar tu sitio. Pero es muy probable que pronto Yahoo y/o MSN también admitan estos pings.contrib.xml$'.si debe tomar un parámetro que corresponde a una palabra clave.contrib.

142 CAPÍTULO 13.7. ½Adelante! revisión 789 del 9 de noviembre de 2009 . seguiremos indagando más profundamente en las herramientas internas que Django nos ofrece. ¾Qué sigue? A continuación. y autenticación. usuarios. El Capítulo 12 examina todas las herramientas que necesitas para proveer sitios personalizados: sesiones. GENERACIÓN DE CONTENIDO NO HTML 13.

Hemos hecho la suposición de que el tráco que visita nuestra web está compuesto por una masa amorfa de usuarios anónimos.. la petición que enviará el navegador se parecerá a esta: Set-Cookie.com Server: GWS/2.com Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 .. claro. la próxima vez que vuelvas a Google. 17-Jan-2038 19:14:07 GMT. No hay persistencia entre una petición y la siguiente. Cuando Google responde. de forma que este la almacena. no es tan fácil como podría parecer. y ninguno de los atributos de la petición (Dirección IP. Fíjate en la línea que comienza con y se lo volverá a enviar a Google cada vez que vuelva a acceder a alguna de sus páginas. identicador del agente..google. se le envía de vuelta la cookie. path=/.Capítulo 14 Sesiones. al menos). Esto no es verdad.. no máquinas. antes o después tendremos que plantearnos como tratar a las personas que están detrás del navegador. GET / HTTP/1. usuarios y altas o inscripciones de los mismos.1 .. Empezaremos al nivel más bajo (cookies ). Este es un hecho importantísimo y que no debemos ignorar: Lo mejor de Internet es que sirve para conectar personas. es decir. expires=Sun. Una cookie es una pequeña cantidad de información que el servidor delega en el navegador. Veamos con un poco más de detalle el funcionamiento. Si queremos desarrollar un sitio web realmente competitivo. Por desgracia.. la respuesta contiene algo parecido a esto: HTTP/1. que se precipitan contra nuestras cuidadosamente diseñadas páginas.1.. domain=.1 Host: google. Cuando abrimos nuestro navegador y escribimos el navegador envía una solicitud HTTP a Google que empieza más o menos así: google.1 200 OK Content-Type: text/html Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671.com. usuario e inscripciones Tenemos que confesar algo: hasta el momento hemos ignorado un aspecto absolutamente importante del desarrollo web. Cookies Los desarrolladores de navegadores hace tiempo que se dieron cuenta de que esta carencia de estados iba a representar un problema para los desarrolladores web. Los navegadores que consultan nuestras páginas tienen a personas reales detrás (la mayor parte del tiempo.1 Host: google. Cada vez que el cliente web solicita una página del servidor. etc.) nos permite discriminar de forma segura y consistente las peticiones de una persona de las del resto. e iremos ascendiendo hasta las herramientas de alto nivel que nos permitirán gestionar sesiones. El navegador almacenará el valor indicado (PREF=ID=5b14f22bdaf1e81c:T GET / HTTP/1. El protocolo HTTP se diseñó especícamente para que fuera un protocolo sin estado. de esa forma.com . y así fue como nacieron las cookies (literalmente galleta ). En este capítulo aprenderemos como solucionar esta carencia de estados. que cada petición y respuesta está totalmente aislada de las demás. 14.. revisión 789 del 9 de noviembre de 2009 .

No obstante. una clave en una tabla de la base de datos que almacene los datos del usuario.COOKIES["favorite_color"]) else: return HttpResponse("You don't have a favorite color. SESIONES... max_age expires path None None "/" La fecha y hora en que la cookie debe expirar. Esto impide que se envíe esta cookie a otras secciones de la web. su valor tiene preferencia sobre el denido mediante max_age.set_cookie("favorite_color". desaparecerá automáticamente cuando se cierre el navegador. Es especialmente útil si no se tiene el control del nivel superior de directorios del servidor web..1: Opciones de las Cookies Parámetro Valor por omisión Descripción El tiempo (en segundos) que la cookie debe permanecer activa. por ejemplo. y te será de utilidad si alguna vez tienes que trabajar con las cookies directamente. Obtener los valores de las cookies que ya están denidas es muy fácil. La ruta o path para la cual es válida la cookie. lo más probable es que uses las prestaciones de alto nivel para la gestión de sesiones y de usuarios. Cómo denir y leer los valores de las cookies A la hora de utilizar las capacidades de persistencia de Django.GET: # Create an HttpResponse object. Si se utiliza este parámetro. lo hace).set_cookie() y que te permiten controlar determinadas características de la cookie. contiene un objeto COOKIES request. Esto debería ayudarte a entender como funcionan el resto de las herramientas que veremos en el capítulo. Los navegadores solo reenviarán la cookie a las páginas que estén en dicha ruta.") Denir los valores de las cookies es sólo un poco más complicado. Con esa información. revisión 789 del 9 de noviembre de 2009 . prestaciones que discutiremos un poco más adelante en este mismo capítulo. que eres la misma persona que accedió un rato antes. response = HttpResponse("Your favorite color is now %s" % \ request. ahora vamos a hacer una breve parada y veremos como leer y denir cookies a bajo nivel. Debes usar el método de tipo como HttpResponse. 14.144 CAPÍTULO 14. Cada objeto de tipo petición.1.GET["favorite_color"]) # . Google puede hacer aparecer tu nombre en la página (De hecho. USUARIO E INSCRIPCIONES Google puede saber ahora. gracias al valor de la Cookie.. Este valor puede ser.COOKIES: return HttpResponse("Your favorite color is %s" % \ request. tal y como se muestra en la tabla 12-1.") Hay una serie de parámetros opcionales que puedes pasar a response. parámetro GET: He aquí un ejemplo que dene la cookie favorite_color set_cookie() en un objeto utilizando el valor que se le pasa def set_color(request): if "favorite_color" in request. request. puedes usarlo para leer cualquier cookie que el navegador haya enviado a la vista: def show_color(request): if "favorite_color" in request. Si este parámetro es la cookie.1. que se comporta como un diccionario. and set a cookie on the response response. Cuadro 14. DD-Mth-YY HH:MM:SS GMT". Debe estar en el formato "Wdy.GET["favorite_color"]) return response else: return HttpResponse("You didn't give a favorite color.

com y aun. Te sorprendería saber cuantos sitios web cometen este tipo de error. El entorno de sesiones de Django Con todas estas limitaciones y agujeros potenciales de seguridad. así que dispone de un entorno de sesiones diseñado para suavizar y facilitar todas estas cuestiones por ti. es obvio que la gestión de las cookies y de las sesiones persistentes es el origen de muchos dolores de cabeza para los desarrolladores web.1. Se puede usar este parámetro para denir una cookie que sea apta para varios dominios. secure False Si este valor se dene como True. A pesar de su uso habitual. Aún más importante. simplemente activa la opción de Avisar antes de aceptar cualquier cookie y date un paseo por Internet. uno de los objetivos de Django es evitar ecazmente estos dolores de cabeza. El entorno de sesiones te permite almacenar y recuperar cualquier dato que quieras basándote en la sesión del usuario.net/mechanize/) que permiten a cualquiera que esté lo sucientemente motivado construir solicitudes HTTP a mano. De hecho. no lleva más de 14. y existen herramientas como mechanize (http://wwwsearch. los navegadores no dan ninguna garantía. La Web rebosa de historias de terror acerca de desarrolladores que guardaron información irrecuperable en las cookies del usuario. Hay otro tipo de ataque. El describe en profundidad este tipo de ataques. EL ENTORNO DE SESIONES DE DJANGO 145 Cuadro 14.com.example.com" la cookie www. se le indica al navegador que só- lo retorne esta cookie a las páginas que se accedan de forma segura (protocolo HTTPS en vez de HTTP). husmear).2. Las Cookies (Especialmente aquellas que no se envían mediante HTTPS) no son seguras. las cookies son el ejemplo perfecto de algo que no es conable. 14. Por supuesto. www2. Esto signica que el desarrollador debe comprobar que el usuario está dispuesto a aceptar las cookies antes de conar en ellas. sino que además la usa para actuar ante el servidor como si fuera el usuario legítimo. nunca debes almacenar información fundamental en las cookies. El error habitual en este escenario consiste en almacenar algo así como unos segundos engañar a sus sistemas de seguridad. la cookie solo será enviada al dominio que la denió. vamos a ver algunos de los más importantes: El almacenamiento de los cookies es voluntario. Aquí. el atacante no solo intercepta la cookie.14. deniendo será enviada a los dominios domain None domain=".example.sourceforge.otro. Así que tampoco debemos almacenar en las cookies datos que sean fáciles de falsicar. Si a este parámetro no se le asigna ningún valor.2. Las Cookies ni siquiera son seguras para los servidores. Por lo tanto. `Capítulo 19`_ IsLoggedIn=1 en una cookie cuando el usuario se ha validado. El resultado de esto es que nunca se debe almacenar información condencial en una cookie. un atacante que tenga acceso al medio puede interceptar la cookie y leer su valor.1: Opciones de las Cookies Parámetro Valor por omisión Descripción El dominio para el cual es válida la cookie. Dado que los datos enviados viajan en texto claro.example.com.subdominio. sgonear.2. Por ejemplo. los navegadores permiten al usuario denir una política de aceptación o rechazo de las mismas. Las cookies tienen doble lo Puede que te hayas dado cuenta de algunos de los problemas potenciales que se presentan con esto de las cookies. Para darte cuenta de lo muy usadas que son las cookies en la web actual. La mayoría de los navegadores permiten manipular y editar de forma sencilla los contenidos de cookies individuales. así como formas de prevenirlo.example. Almacena la información relevante solo en el servidor y abstrae todo el problema del envío y recepción de las revisión 789 del 9 de noviembre de 2009 . lo que se llama ataques de tipo snooping (por snoop. aún más insidioso. conocido como ataque man-in-the-middle o MitM (Ataque de tipo Hombre-en-medio o Intermediario). solo para encontrarse con que el navegador había borrado todos esos datos por cualequier razón. están expuestas a que terceras personas lean esa información.

False): revisión 789 del 9 de noviembre de 2009 .middleware.2. Los valores de las claves de una sesión que empiecen con el carácter subrayado están reservadas para uso interno de Django.py syncdb si lo tuviste que añadir).SessionMiddle en el valor de 'django.session por otro objeto. pero toda ayuda es buena.. y como usarlas en nuestras vistas. etc. Hay dos o tres reglas muy sencillas para usar ecazmente las sesiones en Django: Debes usar sólo cadenas de texto normales como valores de clave en merece la pena seguirla. 14. que se comporta igual que un diccionario. Veamos un ejemplo rápido. Estas solo almacenan una versión codicada (hash ) del identicador de la sesión.146 CAPÍTULO 14. lo cual te aisla de la mayoría de los problemas asociados con las cookies.session["fav_color"] # Clear an item from the session: del request.get('has_commented'.. pero request.this could be called in a different view. sólo hay unas pocas variables así.contrib.session. Activar sesiones Las sesiones se implementan mediante un poco de middleware (véase Capítulo 15) y un modelo Django. Esta vista simplicada dene una variable usuario publique dos veces el mismo comentario: has_commented como True después de que el usuario haya publicado un comentario. Por ejemplo.contrib.session["fav_color"] # Check if the session has a given key: if "fav_color" in request. SessionMiddleware de MIDDLEWARE_CLASSES Esto te ahorrará sólo un poco de sobrecarga. También puedes usar otros métodos propios de un diccionario como keys() o items() en request. por ejemplo. startproject de INSTALLED_APPS (Y Los valores por defecto creados por ya tienes estas dos características habilitadas. eso impedirá que Django pueda interferir con tu aplicación. podrías usar algo # Set a session value: request. Nunca reemplaces request. y ningún otro dato. enteros. deberías quitar la referencia a y borrar 'django. # or many requests later (or both): fav_color = request.1.session. pero. Comprobar que ejecutar MIDDLEWARE_CLASSES de forma que contenga 'django. SESIONES.session["fav_color"] = "blue" # Get a session value -. Usar las sesiones en una vista Cuando están activadas las sesiones.sessions' esté incluido manage. así que a menos que las hayas borrado. Editar el valor de 2. Utilízalo sólo como si fuera un diccionario.sessions' INSTALLED_APPS. y nunca accedas o modiques sus atributos.sessions.contrib. Es una forma sencilla (aunque no particularmente segura) de impedir que el def post_comment(request. En la práctica. es muy probable que no tengas que hacer nada para empezar a usar las sesiones. new_comment): if request. en vez de. Veamos como activar las sesiones. Esto es más un convenio que un regla en el sentido estricto.tendrán un atributo llamado como esto en una de tus vistas: HttpRequest --el primer argumento de cualquier función que actúe session. USUARIO E INSCRIPCIONES cookies. objetos.2.session. Para activar las sesiones. Si lo que quieres en realidad es no usar sesiones.session: . a no ser que sepas lo que estás haciendo (y estés dispuesto a mantenerte al día en los cambios internos de Django).2. lo mejor que puedes hacer es evitar usar el carácter subrayado como prejo en tus propias variables. los objetos como una vista en Django-. Se puede leer y escribir en él de la misma forma en que lo harías con un diccionario normal. 14. necesitas seguir los siguientes pasos: 1.

2..objects. El mecanismo de autenticación que presentaremos un poco más adelante realiza esta tarea de forma mucho más segura y robusta.") Y esta le permite cerrar o salir de la sesión: def logout(request): try: del request. Los ejemplo son deliberadamente simples para que se comprendan con más facilidad.") c = comments.save() request.session.") Nota En la práctica. but since this is an example.method == 'POST': # Check that the test cookie worked (we set it below): if request. un tanto extraña entre las llamadas a set_test_cookie() y test_cookie_worked() se debe a la delete_test_cookie() para limpiar la cookie de prueba después forma es que trabajan las cookies. 14. Comprobar que las cookies sean utilizables Como ya mencionamos.session['has_commented'] = True return HttpResponse('Thanks for your comment!') Esta vista simplicada permite que un usuario se identique como tal en nuestras páginas: def login(request): try: m = Member.") except Member. Django incluye una forma fácil de comprobar que el cliente del usuario disponga de esta capacidad.id return HttpResponse("You're logged in..delete_test_cookie() # In practice. en otra vista request. if request. so delete it. no tienes forma de saber si el navegador la ha aceptado realmente hasta la siguiente solicitud.password == request.2.. el resultado de llamar a request.test_cookie_worked(): # The test cookie worked. Cuando se dene una cookie. we'd need some logic to check username/password # here. EL ENTORNO DE SESIONES DE DJANGO 147 return HttpResponse("You've already commented. Por ello.session.") # The test cookie failed. Es una práctica recomendable llamar a la función He aquí un ejemplo típico de uso: de haberla usado.session. no se puede conar en que el cualquier navegador sea capaz de aceptar cookies.DoesNotExist: return HttpResponse("Your username and password didn't match.. If this revisión 789 del 9 de noviembre de 2009 . def login(request): # If we submitted the form. request.3. esta sería una forma pésima de validar a tus usuarios.14.POST['password']: request.session.session['member_id'] except KeyError: pass return HttpResponse("You're logged out.Comment(comment=new_comment) c. Sólo es necesario llamar a la función Esta división distinta.POST['username']) if m. y comprobar posteriormente. return HttpResponse("You're logged in.get(username__exact=request.set_test_cookie() en una vista. so display an error message.test_cookie_worked(). Lo mejor es hacerlo justo después de haber vericado que las cookies funcionan.session['member_id'] = m.

session. incluso si no se ha modicado ninguno de Fíjate que la cookie de sesión sólo se envía cuando se ha creado o modicado una sesión..models.get_decoded() {'user_id': 42} 14. 13. else: return HttpResponse("Please enable cookies and try again. Dado que es un modelo normal.models import Session >>> s = Session. Usar las sesiones fuera de las vistas pseudo-aleatorio de 32 caracteres. la sección de expiración (expires) .2. request. la cookie de sesión será reenviada en cada petición.get(pk='2b1189a188b44ad18c35e113ac6ceead') >>> s. 35. que django.expire_date datetime. solo almacena la sesión en la base de datos si ésta ha sido modicada. especicando la opción sus valores.sessions. si cualquiera de los valores almacenados en el diccionario es asignado o borrado.session['foo'] instead of request. Esto puede dar lugar a algunos errores sutiles. Cuándo se guardan las sesiones Title underline too short. 12) Para poder acceder a los datos de la sesión. revisión 789 del 9 de noviembre de 2009 SESSION_SAVE_EVERY_REQUEST True. SESIONES.session['foo'] = {} # Gotcha: Session is NOT modified.session['foo'] # Session is modified.' >>> s. en principio.session['foo'] = 'bar' # Session is modified. puedes acceder a las propiedades de las >>> from django. request. Si se actualizará cada vez que se reenvíe la cookie.objects.contrib. las funciones de autenticación ya denidas en el entorno realizan estos chequeos por ti.4.5.session.contrib.2. request. because this alters # request. Django almacenará la sesión en la base de datos en cada petición. Si lo hace- mos así. send the test cookie along with the login form. hay que usar el método que consistían en un diccionario. denido en es el valor que se almacena en la cookie. está como SESSION_SAVE_EVERY_REQUEST a True.session_data 'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj. 14. Esto se debe a que estos datos.") # If we didn't post.datetime(2005.sessions.set_test_cookie() return render_to_response('foo/login_form. 8. 20. >>> s. es decir. como se indica en el último ejemplo: # Session is modified. USUARIO E INSCRIPCIONES # was a real site we'd want to display a friendlier message.html') Nota De nuevo. request.session['foo']['bar'] = 'baz' Se puede cambiar este comportamiento. cada sesión es simplemente un modelo de entidad de Django como cualquier otro..148 CAPÍTULO 14. del request. están almacenados codicados: get_decoded(). Cuándo se guardan las sesiones ----------------------------Django. De forma similar. Cada sesión se identica gracias a un hash sesiones usando la API de acceso a la base de datos de Django: Internamente.

lo que signica que sólo se podrá utilizar mediante el protocolo HTTPS.209. Puede ser cualquier cadena de texto. Cuadro 14.2: Valores de conguración que inuyen en el comportamiento de las cookies Opción Descripción El Dominio a utilizar por la cookie de sesión.2. EL ENTORNO DE SESIONES DE DJANGO 149 14. la cookie se marcará como segura. Se puede controlar el comportamiento del entorno para que use cookies de este tipo. por inválida.com" para utilizar la cookie en diferentes subdominios. Si la cookie no contiene ningún valor de expiración. Se puede utilizar.14.6. Estos valores son adecuados si no quieres obligar a tus usuarios a validarse cada SESSION_EXPIRE_AT_BROWSER_CLOSE True. Django usará cookies que se invalidarán cuando el usuario cierre el navegador.600 segundos). El valor por omisión de la opción serán almacenadas en el navegador del usuario durante vez que abran el navegador y accedan a tu página. hay otros valores de conguración que inuyen en la gestión de sesiones con Django. to Sesiones breves frente a sesiones persistentes Las Cookies pueden incluir opcionalmente una fecha de expiración.2. 14. ajustando en valor de la opción SESSION_EXPIRE_AT_BROWSER_CLOSE. Es posible que te hayas jado en que la cookie que nos envió Google al principio del capítulo contenía el siguiente tex- que informa al navegador del momento en que se debe desechar. expires=Sun. el valor Valor por defecto SESSION_COOKIE_DOMAIN None ". Indica si se debe usar una cookie segura para la cookie de sesión. lo que signica que las cookies SESSION_COOKIE_AGE segundos (Cuyo valor por defecto es de se establece a dos semanas. revisión 789 del 9 de noviembre de 2009 . tal y como se muestra en la tabla 12-2. o 1. El valor None SESSION_COOKIE_NAME SESSION_COOKIE_SECURE indica una cookie estándar.2. Si el valor es "sessionid" False True. Otras características de las sesiones Además de las características ya mencionadas. el navegador entiende que esta debe expirar en el momento en que se cierra el propio navegador. por ejemplo.7.lawrence. Si SESSION_EXPIRE_AT_BROWSER_CLOSE es False. breves. El nombre de la cookie de sesiones.. 17-Jan-2038 19:14:07 GMT.

como recurso extremo en el caso de que no se puedan utilizar las cookies. no podemos simplemente conar en que los usuarios sean quien dicen ser. Si no modicas ningún valor SESSION_SAVE_EVERY_REQUEST de reenvía la cookie como True). Poner los identicadores de sesión en las URL no solo hace que las direcciones sean más feas. Naturalmente. Django nos proporciona las herramientas necesarias para tratar con este problema tan habitual (Y con muchos otros). El sistema también es llamada sistema aut/aut (autenticacación y autorización). revisión 789 del 9 de noviembre de 2009 . el sistema aut/aut de Django consta de los siguientes componentes: Usuarios : Personas registradas en tu sitio web Permisos : Valores binarios (Si/No) que indican si un usuario puede o no realizar una tarea determinada.session. no Django nunca accederá a la base de datos. Usuarios e identicación TM . Véase la documentación del módulo pickle incluido en la biblioteca estándar de Python para más información. ya has modicado las tablas en las que se basa el sistema aut/aut. SESIONES. JSP). 14. necesitamos autenticarlos de alguna manera.contrib. Se necesita: Vericar (autenticación ) que un usuario es quien dice ser (Normalmente comprobando un nombre de usuario y una contraseña contra una tabla de una base de datos) Vericar que el usuario está autorizado (autorización ) a realizar una operación determinada (Normalmente comprobando una tabla de permisos) Siguiendo estos requerimientos. la segunda parte de la ecuación es utilizar esas sesiones para validar al usuario. tratar con los usuarios implica dos procesos.3. (A no ser que hayas denido El entorno de sesiones de Django se basa entera y exclusivamente en las cookies. como hacen otros entornos (PHP. he aquí una serie de notas técnicas acerca de algunos aspectos interesantes de la gestión interna de las sesiones: El diccionario de la sesión acepta cualquier objeto Python capaz de ser serializado con pickled. Las sesiones nos permiten Estamos ya a medio camino de poder conectar los navegadores con la Gente de Verdad almacenar información a lo largo de las diferentes peticiones del navegador. No almacena la información de la sesión en las URL. atributo Los datos de la sesión son suministrados bajo demanda.sessions bastante directo y claro. el código fuente es Referer. Por supuesto. Si nunca accedes al request.150 CAPÍTULO 14. es decir. también hace que el sistema sea vulnerable ante un tipo de ataque en que se roba el identicador de la sesión utilizando la cabecera Si aun te pica la curiosidad. El sistema de autenticación de usuarios de Django maneja cuentas de usuarios. y si has modicado usuarios y grupos con dicha herramienta. El nombre implica que. grupos. grupos : Una forma genérica de aplicar etiquetas y permisos a más de un usuario. Esta es una decisión tomada de forma consciente. Django sólo envía la cookie si tiene que hacerlo. permitirle hacer login. a menudo. Si ya has utilizado la herramienta de administración (descrita en el Capítulo 6). mensajes : Un mecanismo sencillo que permite enviar y mostrar mensajes del sistema usando una cola. mira en para más detalles. habrás visto muchas de estas utilidades. Los datos de la sesión se almacenan en una tabla en la base de datos llamada django_session. django. USUARIO E INSCRIPCIONES Detalles técnicos Para los más curiosos. Perles : Un mecanismo que permite extender los objetos de tipo usuario con campos adicionales. permisos y sesiones basadas en cookies. la sesión.

viene instalado por defecto. 30 caracteres como máximo. Una vez resuelto este tema. dígitos y el carácter subrayado).contrib. está incluido Asegúrate de que en 'django. por lo que es preferible comprobar el resultado user. Al igual que ocurría con las sesiones. Booleano. De igual manera. Django nunca almacena la contraseña en crudo. Obligatorio. Puedes saber fácilmente si el usuario está identicado o no con el método is_authenticated(): if request. ya estamos preparados para empezar a lidiar con los usuarios en nuestras vistas. Sólo acepta caracteres alfanuméricos (letras. Habilitando el soporte para autenticación y necesita ser instalado. pero también puede ser por otros métodos. Cuadro 14.4. False para deshabilitar a un usuario sin tener que borrarlo de la revisión 789 del 9 de noviembre de 2009 .AuthenticationMiddleware' MIDDLEWARE_CLASSES después de SessionMiddleware.is_authenticated() antes de asumir de buena fe que nos encontramos ante un usuario legítimo. por lo que solo es necesario django. else: # Do something for anonymous users. de los objetos de la clase User. pero no toda.3. respectivamente. Los objetos AnonymousUser emulan parte de esta interfaz.user. Seguir la pista de los usuario implica usar cookies. Opcional. 14. Un código de comprobación (hash ).is_authenticated(): # Do something for authenticated users.contrib. tal y como se explico previamente en este capítulo. junto con otros metadatos de la contraseña. Si no hay ningún usuario conectado. Fecha y hora en que fue creada esta cuenta de usuario.py syncdb. 30 caracteres como máximo. La principal interfaz que usarás para trabajar con los datos del usuario dentro de una vista es una instancia de la clase que representa al usuario que está conectado en ese momento.auth. Dirección de correo electrónico. Una vez que ya tienes un usuario (normalmente mediante de la clase de que se describirán en breve) dispondrás de una serie de campos de datos y métodos asociados al mismo. Incluye 'django.3: Campos de los objetos User Campo Descripción Obligatorio. Se puede poner a tabla.contrib. este objeto será request. Indica que el usuario puede acceder a las secciones de administración. aún cuando no se le hayan asignado explícitamente Fecha y hora de la última vez que el usuario se identicó. Utilizando usuarios request.4. y por lo tanto necesitamos el entorno de sesiones operativo. Las tablas 12-3 y 12-4 listan todos los campos y métodos. Indica que la cuenta puede ser usada para identicarse.auth' dentro de tu INSTALLED_APPS y ejecuta manage.user. Se asigna automáticamente a la fecha actual por defecto. Opcional.1. UTILIZANDO USUARIOS 151 14.user. Señala que el usuario tiene todos los permisos. el sistema de autenticación viene incluido como una aplicación en el módulo seguir los siguientes pasos si previamente la has desinstalado: Comprueba que el sistema de sesiones esté activo. Booleano. Véase la sección  Cambia contraseñas para más información Booleano. Se asigna automáticamente a la fecha actual en su momento. es un objeto AnonymousUser (Veremos más sobre esta clase un poco más adelante). 30 caracteres como máximo.14.middleware. username first_name last_name email password is_staff is_active is_superuser last_login date_joined Opcional.

El mensaje aparece DEFAULT_FROM_EMAIL.. obtenidos a través del grupo o grupos a las que pertenezca.add(group1.groups = group_list # Add a user to some groups: myuser.. En general. siempre devolverá False. con un espacio en medio. app_label. y los borra posteriormente. Este método no guarda el objeto User. or ción de remite distinta.4: Métodos de los objetos User Método Descripción Siempre devuelve is_authenticated() True para usuario reales. y tampoco comprueba que la cuenta esté activa. Se puede acceder a estos objetos relacionados de la misma manera en que se usan otros campos múltiples: # Set a user's groups: myuser. msg) Devuelve una lista con los permisos que tiene un usuario. Se le parámetro opcional. Si el usuario no está activo. los objetos de tipo User mantienen dos campos de relaciones múltiples o muchos-a-muchos: Grupos y permissions). USUARIO E INSCRIPCIONES Cuadro 14.codename". El perm está en el formato `"package. get_group_permissions() get_all_permissions() has_perm(perm) has_perms(perm_list) has_module_perms(app_label) get_and_delete_messages() email_user(subj. Devuelve una lista de mensajes (objetos de la clase Message) Devuelve valor de de la cola del usuario. esto no implica que posea ningún permiso. realizando internamente las operaciones necesarias para calcular el código de comprobación o hash necesario.152 CAPÍTULO 14. Devuelve una lista con los permisos que tiene concedidos un usuario. Si el usuario no está activo. Es una forma de determinar si el usuario se ha identicado. check_password(passwd) devuelve True si la cadena de texto en claro que se le pasa coincide con la contraseña del usuario. siempre devolverá False.) revisión 789 del 9 de noviembre de 2009 . Devuelve Cambia la contraseña del usuario a la cadena de texto en claro indicada.groups. Devuelve True si el usuario tiene algún permiso en la etiqueta de aplicación indicada. from_email. En la sección  Perles se explicará con más detalle este método. y ``False` para usuarios "reales". Si el usuario no está activo. como enviado desde la dirección indicada pasar en un el val- puede tercer para indicar otra direc- get_profile() Devuelve un perl del usuario especíco para el sitio. Devuelve True si el usuario tiene todos los permisos indicados. Devuelve la concatenación de los campos first_name y last_name. SESIONES.. group2. es preferible usar el método `is_authenticated(). Realiza internamente las operaciones necesarias para calcular los códigos de comprobación o hash necesarios. Sólo indica que el usuario se ha identicado con éxito. is_anonymous() get_full_name() set_password(passwd) True` sólo para usuarios anónimos. siempre devolverá False. ya sea a través de los grupos a los que pertenece o bien asignados directamente. True si el usuario tiene el permiso indicado. permisos (groups y Por último. Envía un correo electrónico al usuario.

.logout(request) # Redirect to a success page. y devuelve un objeto de tipo User authenticate().1.POST['username'] password = request..permissions.) myuser.login(request.remove(group1. that's wrong!" authenticate() sólo verica las credenciales del usuario. .authenticate(username=username.permissions. return HttpResponseRedirect("/account/loggedout/") revisión 789 del 9 de noviembre de 2009 . La llamada a login() para objeto User y La llamada a login() acepta un objeto de la clase y HttpRequest y un almacena el identicador del usuario en la sesión. esta función si la contraseña es correcta para el identicador de usuario. usando el entorno de sesiones de Django.auth: authenticate() username y y login()..14. Necesita que se le from django.. authenticate() login(). return HttpResponseRedirect("/account/loggedin/") else: # Show an error page return HttpResponseRedirect("/account/invalid/") Para cerrar la sesión.contrib.permissions.. password. dentro de una vista: from django.. .clear() # Permissions work the same way myuser. Django incluye dos funciones para realizar estas acciones.) myuser.add(permission1. se puede llamar a pase como parámetro un objeto de tipo django.logout() dentro HttpRequest. veremos como hacer que los usuario pueden iniciar y cerrar la sesión a mano. password=password) if user is not None and user..contrib. en el módulo django. permission2. permission2. El siguiente ejemplo muestra el uso de ambas funciones. Pero antes de entrar en detalles. and the user is marked "active" auth.authenticate(username='john'.contrib import auth def logout(request): auth. from django.. group2.. Iniciar y cerrar sesión Django proporciona vistas predenidas para gestionar la entrada del usuario.remove(permission1.POST['password'] user = auth.contrib import auth user = auth.auth. además de otros trucos ingeniosos.. UTILIZANDO USUARIOS 153 # Remove a user from some groups: myuser.4.is_active: # Correct password. . Para autenticar un identicador de usuario y una contraseña. se utiliza la función acepta dos parámetros .groups. cuando cierra la sesión).groups. (el momento en que se identica). Si falla la comprobación (ya sea porque sea incorrecta la contraseña o porque sea incorrecta la identicación del usuario)..4. . y no devuelve ningún valor: de una vista.. password='secret') if user is not None: print "Correct!" else: print "Oops.. (es decir. y la salida. la función devolverá None: >>> >>> >>> .) # Remove a user from all groups: myuser.clear() 14. Todavía hay que realizar una llamada a completar el inicio de sesión. user) # Redirect to a success page.contrib import auth def login(request): username = request.permissions = permission_list myuser.

logout). la razón de haber implementado todo este sistema es permitirnos limitar el acceso a determinadas partes de nuestro sitio. (r'^accounts/logout/$'..' method='post'> <label for="username">User name:</label> <input type="text" name="username" value="" id="username"> <label for="password">Password:</label> <input type="password" name="password" value="" id="password"> <input type="submit" value="login" /> <input type="hidden" name="next" value="{{ next|escape }}" /> <form action='.html" %} { % block content %} { % if form.is_authentic from django. Puedes indicar una next. El primer paso para utilizar las vistas de autenticación es mapearlas en tu URLconf. login). Necesitas modicar tu código hasta tener algo parecido a esto: from django.contrib. that's not a valid username or password</p> { % endif %} <form action='.154 CAPÍTULO 14. También identicación y se añadirá automáticamente su valor ahora en un campo oculto.4.user. no es normalmente necesario escribir tus propias funciones para realizar estas tareas. La forma más simple y directa de limitar este acceso es comprobar el resultado de llamar a la función y redirigir a una página de identicación. template_name). llamado debe redirigir una vez efectuado el cierre de la sesión. que indicaría la vista a la que se 14. SESIONES. aun si no hubiera ningún usuario conectado..html (puedes cambiar el nombre de la plantilla utilizando un parámetro opcional. si procede: request.auth. next_page. No obstante. (r'^accounts/login/$'.html La vista de cierre de sesión se comporta de forma un poco diferente. el sistema de autenticación viene con un conjunto de vistas predenidas para ello. su navegador será redirigido a a redireccionar después de la identicación. USUARIO E INSCRIPCIONES La llamada a logout() no produce ningún error. la vista de /accounts/login/ { % extends "base.views import login. login utiliza la plantilla denida en registration/login.' method='post'> { % endblock %} Si el usuario se identica correctamente.2. En la práctica. que puedes incluir registration/logged_out. se puede llamar a esta vista con un parámetro extra. Por defecto utiliza la plantilla denida (Que normalmente contiene un mensaje del tipo Ha cerrado su sesión). # existing patterns here. ) /accounts/logout/ son las URL por defecto que usa Django para estas vistas.http import HttpResponseRedirect def my_view(request): if not request. en dirección distinta especicando un tercer campo (normalmente oculto) que se llame /accounts/profile/. Una plantilla de ejemplo podría ser esta: y Por defecto.user. cuyo valor debe ser la URL puedes pasar este valor como un parámetro GET a la vista de al contexto en una variable llamada next. logout urlpatterns = patterns(''. El formulario necesita contener un campo llamado username y otro llamado password. Limitar el acceso a los usuarios identicados Por supuesto.errors %} <p class="error">Sorry.is_authenticated(): revisión 789 del 9 de noviembre de 2009 .

is_authenticated(): return render_to_response('myapp/login_error. tenga asignado el permiso esto de los permisos con más detalle dentro de poco ) haríamos: polls. La forma más cruda es ejecutar las pruebas que queremos hacer directamente en el código de la vista.4.path) # .has_perm('polls. Si se desea abreviar. login_url="/login/") def vote(request): # .") De nuevo. el ejemplo anterior from django. O quizás mostrar un mensaje de error: def my_view(request): if not request.user.is_authenticated() and request.has_perm("polls.. Django proporciona una forma abreviada llamada user_passes_test.user. además.4. que te permite indicar la url usuario esté identicado. así que Django proporciona una permission_required(). Usando este decorador. revisión 789 del 9 de noviembre de 2009 . Por ejemplo..auth.can_vote (Se explicará def vote(request): if request. incluyendo la url /accounts/login/?next=/polls/3/. y devuelva True si el usuario puede acceder caso contrario. .contrib.14. En este ejemplo.. Esto es lo que hace el decorador login_required: next... Requiere que se la pasen unos argumentos y genera un decorador especializado para cada situación en particular: def user_can_vote(user): return user.. ejecuta la vista sin ningún cambio. forma abreviada para estos casos: El decorador se podría codicar así: Comprobar si un usuario posee un determinado permiso es una tarea muy frecuente. o proporcionar una página de identicación distinta de la vista por defecto.can_vote") @user_passes_test(user_can_vote..can_vote')): # vote here else: return HttpResponse("You can't vote in this poll. y las dos cosas se hacen de manera similar. El decorador y función) y que a su vez acepte como parámetro un objeto del tipo False en user_passes_test tiene un parámetro obligatorio: un objeto que se pueda llamar (normalmente una User.decorators import permission_required @permission_required('polls.contrib.can_vote'.is_authenticated() and user.decorators import login_required @login_required def my_view(request): # .. por ejemplo Si el usuario no está identicado.. para comprobar que el usuario está identicado y que. hemos usado también un segundo parámetro opcional. de la página que el usuario debe utilizar para identicarse (/accounts/login/ por defecto). La vista puede asumir sin problemas que el usuario esta identicado correctamente 14. actual Si el usuario está identicado.3.auth. esa es una comprobación que se debe hacer explícitamente. Limitar el acceso a usuarios que pasan una prueba Se puede limitar el acceso basándose en ciertos permisos o en algún otro tipo de prueba. Es importante destacar que user_passes_test no comprueba automáticamente que el login_url. login_url="/login/") def vote(request): # Code here can assume a logged-in user with the correct permission.html') # . se puede usar el decorador login_required sobre las vistas que nos interese proteger: from django..user. redirige a la dirección como un parámetro con el nombre /accounts/login/. UTILIZANDO USUARIOS 155 return HttpResponseRedirect('/login/?next= %s' % request.

el tipo de hash.models import User >>> user = User. permisos y grupos La forma más fácil de gestionar el sistema de autenticación es a través de la interfaz de administración y la mayor parte del tiempo esa es la forma más adecuada de gestión. el algoritmo usado para realizar una transformación hash de un solo sentido sobre la contraseña. .generic.auth.set_password('goo goo goo joob') >>> user. separados entre si por el carácter dolar ($). admin. >>> user. Para ser más exactos.. 14. el grano de sal (salt ) y el código hash propiamente dicho.com'.. como es lógico.list_detail import object_detail @login_required def limited_object_detail(*args. también acepta el parámetro opcional login_url. Crear usuarios Puedes crear usuarios con el método create_user: >>> from django.decorators import login_required from django. **kwargs) Puedes cambiar el decorador login_required por cualquier otro que quieras usar. Gestionar usuarios. SESIONES. preparada para ser almacenada en la base de datos Este te permite cambiar algunos de sus atributos antes de guardarlos. User...get(username='john') >>> user. La contraseña se almacena en la base de datos en forma de código de comprobación (salted hash ) y. hace falta un mayor control. Por ejemplo: revisión 789 del 9 de noviembre de 2009 . El grano de sal es una cadena de texto aleatoria que se utiliza para aumentar la resistencia de esta codicación frente a un ataque por diccionario. tienes que usar un recubrimiento sencillo alrededor de la vista que quieres proteger. **kwargs): return object_detail(*args.objects.objects. El Capítulo 6 describe como usar esta interfaz para modicar los datos de los usuarios y controlar sus permisos y accesos.save() No debes modicar directamente el atributo modicada sólo a través de este método. de nuevo con el valor Limitar el acceso a vistas genéricas Una de las preguntas más frecuentes en la lista de usuarios de Django trata de cómo limitar el acceso a una vista genérica.156 CAPÍTULO 14.contrib.4. debe ser password de un objeto User es una cadena de texto con el siguiente formato: hashtype$salt$hash Es decir.create_user(username='john'.contrib. A veces.auth. .save() Cambia contraseñas Puedes cambiar las contraseña de un usuario llamando a set_password(): >>> user = User.4. por tanto. email='jlennon@beatles. a no ser que tengas muy claro lo que estás haciendo. no obstante. y apuntar en tu URLconf al recubrimiento en vez de a la vista genérica: from dango. password='glass onion') (create_user() no llama al método si quieres: En este momento. USUARIO E INSCRIPCIONES El decorador permission_required() /accounts/login/ en caso de omisión. Para conseguirlo. el atributo password. y para eso podemos utilizar las llamadas a bajo nivel que describiremos en este capítulo. El valor de hashtype puede ser sha1 (por defecto) o md5. user es una instancia de la clase save()).is_staff = True >>> user.views.

Para aumentar la seguridad. pero es prácticamente imposible reconstruir el valor original partiendo únicamente del código hash. pero menos de lo que parece. al añadir el grano de sal los códigos hash resultantes serán diferentes. {} return render_to_response("registration/register. El alta del usuario Podemos usar estas herramientas de bajo nivel para crear vistas que permitan al usuario darse de alta.get_validation_errors(data) if not errors: new_user = form. La forma más sencilla es escribir una pequeña vista que pregunte al usuario los datos que necesita y con ellos se cree directamente el usuario.copy() errors = form. es fácil calcular el código hash de un determinado valor.shortcuts import render_to_response django.POST.save(data) return HttpResponseRedirect("/books/") else: data. los ordenadores son increíblemente rápidos. ¾Tengo que echar sal a mi ordenador? No. que a su vez es más complicado al haber aumentado la entropía con el grano de sal. Afortunadamente. Este llevará algo de tiempo. por lo que Django da la opción de crearte tu propia vista para ello. Para empeorar las cosas.14. lo que obliga al atacante a volver al sistema de ataque por fuerza bruta. calculando los códigos hash de millones de contraseñas distintas y comparando esos códigos con los que están almacenados en la base de datos.4. UTILIZANDO USUARIOS 157 sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4 Las funciones User. la más segura posible. ofrece un buen compromiso entre seguridad y conveniencia.forms import UserCreationForm def register(request): form = UserCreationForm() if request.set_password() y User. que consisten en valores hash precalculados de millones de contraseñas de uso habitual.html".method == 'POST': data = request. un atacante que pudiera acceder a la base de datos podría ahora realizar un ataque por fuerza bruta. Prácticamente todos los desarrolladores quieren implementar el alta del usuario a su manera. hay disponibles públicamente lo que se conoce como tablas arco iris (rainbow tables ). la sal de la que hablamos no tiene nada que ver con ninguna receta de cocina. Django proporciona un formulario prefabricado que se puede usar con este n. se añade un valor inicial aleatorio y diferente a cada contraseña antes de obtener el código hash. Como cada grano de sal es diferente para cada password se evita el uso de tablas arco iris. No obstante. que se caracteriza por ser de un solo sentido. Este valor aleatorio es el grano de sal. es muy fácil de hacer. cualquiera que pudiera obtener acceso a la base de datos podría saber sin ninguna dicultad todas las contraseñas al instante. Usando una tabla arco iris.http import HttpResponseRedirect django. es decir.check_password() manejan todos estos detalles y comprobaciones de forma transparente. Si almacenáramos las contraseñas como texto en claro. Aunque esta técnica no es. Otra ventaja es que si dos usuarios eligen la misma contraseña. un atacante puede romper la mayoría de las contraseñas en segundos. Una función hash es una función criptográca.contrib. { revisión 789 del 9 de noviembre de 2009 . errors = {}. como se muestra en el siguiente ejemplo: from from from from django import oldforms as forms django. en términos absolutos.auth. es una forma habitual de aumentar la seguridad a la hora de almacenar una contraseña. Al guardar las contraseñas en forma de códigos hash se reduce el peligro en caso de que se comprometa la seguridad de la base de datos.

USUARIO E INSCRIPCIONES }) 'form' : forms.djangoproject.96/forms/ para más detalles sobre oldforms. Véase http://www.com/documentation/0. así como sus permisos. errors) registration/register.auth" en si usas el hablando.html_error_list }} { % endif %} <label for="id_password1">Password: {{ form.errors %} {{ form. data. (véase Usar información de autenticación en plantillas El usuario actual. esa plantilla podría consistir Este formulario asume que existe una plantilla llamada en algo parecido a esto: { % extends "base. están disponibles en el contexto de la plantilla cuando usas RequestContext `Capítulo 10`_).forms. será completada muy pronto. SESIONES.username. 14. el usuario actual (Ya sea una instancia de User o de AnonymousUser) es accesible {{ user }}: { % if user.FormWrapper(form. incluido que es el valor que viene predenido cuando se crea un proyecto.html" %} { % block title %}Create an account{ % endblock %} { % block content %} <h1>Create an account</h1> <form action=".158 CAPÍTULO 14.html_error_list }} { % endif %} <label for="id_username">Username:</label> {{ form.username }}.</p> revisión 789 del 9 de noviembre de 2009 .context_processors.5. a la hora de publicar esto.errors %} {{ form.username. Nota Técnicamente tilla sólo RequestContext y en "django. Cuando se usa en la plantilla con el nombre RequestContext.UserCreationForm es. Thanks for logging in.errors %} {{ form.contrib.password2.password2 }} <input type="submit" value="Create the account" /> </label> { % endblock %} Nota django. véase `Capítulo 10`_ para más información.error_dict %} <p class="error">Please correct the errors below. estas variables están disponibles la la opción en el contexto está de la el planvalor conguración TEMPLATE_CONTEXT_PROCESSORS.password1.password1.is_authenticated %} <p>Welcome.</p> { % endif %} { % if form.password2. Como ya se comentó.core.password1 }} { % if form. un formulario del estilo oldforms." method="post"> { % if form. La transición al nuevo sistema.auth. {{ user.html.username }} { % if form.4.html_error_list }} { % endif %} <label for="id_password2">Password (again): {{ form. tal y como se explica en el capítulo siete.

Si no la tiene.<action>_<object_name>". fecha de publicación o identicador. Puedes usar {{ perms. debes ejecutar otra vez syncdb para crear los permisos.5. o se puede usar una forma más especíca.polls %} <p>You have permission to do something in the polls app.change_respuesta" y "encuestas. usando el atributo permissions en la clase Meta. se crearan automáticamente los tres permisos con los nombres "encuestas. Si has inicializado la base de datos y has añadido la clase Admin con posterioridad. añadir. cambiar y borrar. perms.1.</p> { % endif %} Los permisos del usuario se almacenan en la variable Hay dos formas de usar este objeto {{ perms }}. Permisos Los permisos son una forma sencilla de marcar que determinados usuarios o grupos pueden realizar una acción. GRUPOS. con un modelo llamado Respuesta. MENSAJES Y PERFILES 159 { % else %} <p>Welcome. En esta sección las veremos con un poco más de detalle. aplicación llamada Admin.14. no se crearán los permisos. los permisos se agregan a la tabla auth_permission cuando ejecutas "<app>. El Estos permisos se crean con el siguiente formato: siguiente ejemplo crea tres permisos hechos a medida: class USCitizen(models. Se usan normalmente para la parte de administración de Django. está limitado a los usuarios que tengan el permiso add para ese tipo de objeto.</p> { % endif %} 14. se pueden usar estas comprobaciones en sentencias { % if perms. 14. si tienes una encuestas.5.5.. Por ejemplo. También puedes denir tus propios permisos para un modelo. se puede decir María puede modicar los reportajes nuevos. Por lo tanto. El sistema de administración de Django utiliza los siguientes permisos: Acceso a visualizar el formulario Añadir. new user.can_vote { % if %}: para comprobar si el usuario tiene concedido un permiso en concreto. Hay que tener cuidado de que el modelo tenga creada una clase Admin a la hora de ejecutar syncdb. ver el formulario de cambios y cambiar un objeto está limitado a los usuarios que tengan el permisos change para ese tipo de objeto.polls. Por ejemplo.delete_respuesta". no a nivel de instancias. "encuestas. Los permisos se denen a nivel de las clases o tipos de objetos.Model): # . grupos. se crean automáticamente para cualquier modelo Django que incluya una clase manage. Please log in. gún permiso para una determinada aplicación. EL RESTO DE DETALLES: PERMISOS.</p> { % if perms.py syncdb.polls }} para comprobar si un usuario tienen al{{ perms. es una forma simplicada de acceder a un par de métodos sobre los permisos que veremos en breve. ni María sólo puede cambiar los reportajes que tengan un determinado estado.add_respuesta". pero puedes usarlos también en tu código. como }}. mensajes y perles Hay unas cuantas cosas que pertenecen al entorno de autenticación y que hasta ahora sólo hemos podido ver de pasada. class Meta: permissions = ( # Permission identifier ("can_drive". revisión 789 del 9 de noviembre de 2009 .can_vote %} <p>You can vote!</p> { % endif %} { % else %} <p>You don't have permission to do anything in the polls app. Borrar objetos está limitado a los usuarios que tengan el permiso delete para ese tipo de objeto. Estos tres permisos básicos. human-readable permission name "Can drive"). El acceso a la lista de cambios. En realidad. Entre bambalinas. El resto de detalles: permisos. pero no María solo puede modicar los reportajes nuevos que haya creado ella. y Añadir objetos.polls..

create(message='message_text').160 CAPÍTULO 14. Es responsabilidad tuya comprobar estos permisos en tus Esto permisos sólo se crearán cuando ejecutes Igual que con los usuarios. los mensajes del usuario actual.message_set. Por ejemplo. verás que aparece un mensaje en lo alto de la página de administración. al igual que en el caso anterior. están accesibles desde la variable de {{ messages }}.. o para enviarles django.html". cualquier usuario que pertenezca a dicho grupo también tiene ese permiso. los permisos se implementa en un modelo Django que reside en el módulo quieras. en cualquier caso. songs): # Create the playlist with the given songs. se puede crear un grupo un correo electrónico sólo a ellos. Por ejemplo.message_set. la cual retorna una lista En el siguiente ejemplo. Las llamadas de la API son bastante simples: Para crear un nuevo mensaje usa Para recuperar/eliminar de objetos Message en la cola del usuario (si es que existiera alguno) y elimina el mensaje de la misma.. Mensajes El sistema de mensajes es un forma muy ligera y sencilla de enviarle mensajes a un usuario.3. # . ("can_vote".5. La interfaz de administración de Django usa los mensajes para noticar que determinadas acciones han podido ser llevadas a cabo con éxito.auth. ("can_drink". la vista guarda un mensaje para el usuario después de crear una lista de reproducción: def create_playlist(request. son modelos Django que residen en el módulo a bajo nivel. user." ) return render_to_response("playlists/create.user. al crear un objeto. mensajes usa user. Usuarios especiales. El siguiente ejemplo representa un fragmento de código que muestras los { % if messages %} <ul> { % for message in messages %} <li>{{ message }}</li> { % endfor %} revisión 789 del 9 de noviembre de 2009 . SESIONES. Cada usuario tiene asociada una cola de mensajes.create( message="Your playlist was added successfully. indicando que se ha podido crear el objeto sin problemas. "Can vote in elections"). Grupos Los grupos son una forma genérica de trabajar con varios usuarios a la vez. Un usuario puede pertenecer a varios grupos a la vez. Los grupos también son una forma cómoda de categorizar a los usuarios para asignarles una determinada etiqueta. Un usuario que pertenezca a un grupo recibe automáticamente todos los permisos que se la hayan otorgado al grupo. context_instance=RequestContext(request)) Al usar mensajes: contexto usando el nombre RequestContext. request. si los tuviera. la manera más sencilla de gestionar los grupos es usando la interfaz de administración de Django. si el grupo Editores tiene el permiso can_edit_home_page. de forma que se les pueda asignar permisos o etiquetas en bloque. puedes usar la API de acceso a la base de datos para trabajar con los grupos 14. o para otorgarles una funcionalidad extra. Por ejemplo.model Esto signica que puedes usar la API de acceso a la base de datos para interactuar con los permisos de la forma que 14. USUARIO E INSCRIPCIONES ) vistas.models así que.5.get_and_delete_messages().contrib. syncdb.auth. Al igual que con los usuarios. django. Los mensajes no tienen ni fecha de caducidad ni fecha de envío.2. Los grupos. de forma que los mensajes lleguen en el orden en que fueron enviados.contrib. y utilizar código para permitir el acceso a determinadas porciones de tu sitio sólo a los miembros de ese grupo. "Can drink alcohol"). Puedes usar la misma API para enviar o mostrar mensajes en tu propia aplicación.

se puede acceder al perl del usuario llamando a una excepción de tipo DoesNotExist user.CharField(maxlength=100. otros entornos denen una serie de campos extra.6. ha optado por proporcionar un mecanismo sencillo que permita crear un perl con los datos que queramos. veamos primero el problema que se supone tiene que resolver. Para entender mejor que es este sistema y para que sirve. El resumen sería este: Muchos sitios en Internet necesitan almacenar más información acerca de sus usuarios de la que está disponible en un objeto de la clase User.contrib. por el contrario. agradecerás tener todas las utilidades posibles a mano. pero si alguna vez tienes que gestionar interacciones complicadas con los usuarios. 14. Esta función elevará si el usuario no tiene creado el perl (puedes atrapar esta excepción y crear el perl en ese momento). El primer paso para crear un perl es denir el modelo que va a almacenar la información que queremos guardar. blank=True) lucky_number = models. favorite_band = models. y que queda enlazado con la cuenta de usuario. pondrías esto en tu chero de conguración: AUTH_PROFILE_MODULE = "myapp. El siguiente ejemplo muestra un perl absolutamente from django. de forma muy sencilla. el campo debe llamarse ForeignKey. El único requerimiento que Django impone a este modelo es que disponga de una campo de tipo sea único (unique=True) y que lo vincule con el modelo arbitrario: User.models import User class MySiteProfile(models.db import models from django.auth.5.14. ¾Qué sigue? Si. blank=True) favorite_cheese = models.. el sistema de mensajería sólo funciona para usuarios de la base de datos. 14. Por último. En el `próximo capítulo`_. Para resolver este problema. ½Hasta el innito y más allá! Duplicate explicit target name: próximo capítulo. aún si no se muestran en pantalla.mysiteprofile" Una vez hecho esto. La mayor parte de las veces no tendrás que preocuparte por todos los detalles que se describen en este capítulo. echaremos un vistazo a una parte de Django que necesita la infraestructura que proporciona el sistema de usuarios/sesiones de Django: la aplicación de comentarios.get_profile().4.a cualquier tipo de objeto que queramos.6. si el perl que denimos en el ejemplo anterior residiera en una aplicación llamada myapp. Además. Así.IntegerField() AUTH_PROFILE_MODULE El siguiente paso es decirle a Django donde buscar estos perles.ForeignKey(User. puedes usar tantos y tan variados campos de datos como quieres. ¾QUÉ SIGUE? 161 </ul> { % endif %} Hay que hacer notar que RequestContext llama a get_and_delete_messages de forma implícita. revisión 789 del 9 de noviembre de 2009 . un completo sistema de comentarios -por parte de usuarios anónimos o identicados. Django. Esta aplicación permite añadir. Para enviar mensajes a usuarios anónimos hay que usar en entorno de sesiones directamente. unique=True) # The rest is completely up to you.CharField(maxlength=100. Perles La parte nal de nuestro puzzle consiste en el sistema de perles.. Aparte de que eso. Para ello asigna a la variable de conguración el identicador de tu modelo. la verdad es que el sistema de autorización tiene tela que cortar.Model): # This is the only required field user = models. y también se pueden gestionar diferentes perles para sitios diferentes usando la misma base de datos. user. Estos perles pueden incluso ser diferentes según cada proyecto. por lo que los mensajes serán borrados.

162 CAPÍTULO 14. SESIONES. USUARIO E INSCRIPCIONES revisión 789 del 9 de noviembre de 2009 .

Django también trabaja muy bien con caches de upstream. en las que las páginas son servidas directamente a la Web.squid-cache. La mayoría de las aplicaciones Web no son el washingtonpost. porque carece del trabajo de tocar los mismos. que son dinámicos.org/) y las caches de los navegadores. son de un tamaño pequeño a uno mediano. tales como Squid (http://www. Las siguientes secciones explican usas cache y no todos los valores revisión 789 del 9 de noviembre de 2009 .1. es precisamente eso. buscar esa página en la cache si la página está en la cache: devolver la página en cache si no: generar la página guardar la página generada en la cache (para la próxima vez) devolver la página generada Django incluye un sistema de cache robusto que permite guardar páginas dinámicas para que no tengan que ser recalculadas cada vez que se piden. Aquí mostramos un pseudocódigo explicando como podría funcionar esto para una página Web dinámica: dada una URL. tendrás que decirle donde vivirán los datos de tu cache. Pero para los sitios con tráco de medio a alto es esencial bajar lo más que se pueda el costo de procesamiento. Por conveniencia. He aquí cuando realizar un cache es de mucha ayuda.com o Slashdot. A saber. sólo las piezas que son difíciles de producir. Puedes dejar en cache el resultado de diferentes vistas. Esta es una decisión importante que afecta el rendimiento de tu cache (si.Capítulo 15 Cache Los sitios Web estáticos. Esto es costoso desde el punto de vista del sobreprocesamiento. Sigue leyendo para descubrir como usar el sistema de cache de Django. o directamente en memoria. lógica de negocio--para crear la página que el visitante nalmente ve. especicas CACHE_BACKEND en tu archivo de conguración. Tus preferencias acerca de la cache van en usará CACHE_BACKEND. si es en una base de datos. Para la mayoría de las aplicaciones Web. renderizado de plantillas. Cuando tu sitio se parezca cada vez más a Slashdot. y con poco tráco. Colocar en cache algo signica guardar el resultado de un cálculo costoso para que no se tenga que realizar el mismo la próxima vez. o se puede dejar en cache el sitio entero. Activar el Cache El sistema de cache requiere sólo una pequeña conguración. 15. La cache en memoria generalmente será mucho más rápida que la cache en el sistema de archivos o la cache en una base de datos. Si simple:/// por omisión. algunos tipos de cache son más rápidos que otros). Django ofrece diferentes niveles de granularidad de cache. en el sistema de archivos. esta sobrecarga no es gran cosa. generan un gran escalamiento. Una gran desventaja en los sitios Web dinámicos. estarás contento de entender este material. Estos son los tipos de cache que no controlas directamente pero a las cuales puedes proveerles algunas pistas (vía cabeceras HTTP) acerca de qué partes de tu sitio deben ser colocadas en cache y cómo. el servidor realiza una serie de cálculos--consultas a una base de datos. Cada vez que un usuario pide una página. Django disponibles para CACHE_BACKEND.

0. En el siguiente ejemplo. 172.244:11213/' Una última observación acerca de Memcached es que la cache basada en memoria tiene una importante desventaja.1. Como los datos de la cache son guardados en memoria.240:11211. Por lejos la más rápida.2. No podrás usar una base de datos diferente para tal. por lo tanto no te quedes solamente con una cache basada en memoria. la cache es compartida en diferentes instancias de Memcached corriendo en las direcciones IP 172. la memoria no es para almacenamiento permanente. ninguno de los sistemas de cache de Django debe ser utilizado para almacenamiento permanente-son todos una solución para la cache.com/) y subsecuentemente por Danga Interactive (http://danga.19.1:11211/' Una muy buena característica de Memcached es su habilidad de compartir la cache en varios servidores. tienes que crear una tabla en tu base de datos y apuntar el sistema de cache de Django a ella. Para sacar provecho de esta característica con Django. Su característica principal es proveer una interfaz--una super-liviana-y- rápida interfaz--para añadir. Esto signica que puedes correr demonios de Memcached en diferentes máquinas.19. Memcached enteramente en memoria. siempre y cuando sea un nombre válido para una tabla y que no esté ya en uso en tu base de datos.0. Dichos bindings vienen en un módulo de Python.242:11211/' En el siguiente ejemplo.244 (puerto 11213): CACHE_BACKEND = 'memcached://172.172. no vienen con Django.19. Corre como un demonio y se le asigna una cantidad especíca de memoria RAM.com/). donde de la tabla en la base de datos. crea la tabla de cache corriendo el siguiente comando: python manage.240 (puerto 11211).19. la cache es compartida en varias instancias de Memcached en las direcciones IP 172.26.240 y 172. incluye todas las direcciones de los servidores en separados por punto y coma. Cache en Base de datos Para usar una tabla de una base de datos como cache. Este comando crea una única tabla en tu base de datos con un formato apropiado para el sistema de cache de Django.0. el tipo de cache más eciente para Django. los cuales memcache. En el siguiente ejemplo.126.242 (puerto 11212) y 172. sin la necesidad de duplicar los valores de la cache en cada máquina.26. Para usar Memcached con Django. no para almacenamiento--pero hacemos hincapié aquí porque la cache basada en memoria es particularmente temporaria.py. por lo tanto no existe sobrecarga de uso en una base de datos o en el sistema de archivos.tummy. el nombre de la tabla para el CACHE_BACKEND = 'db://mi_tabla_cache' El sistema de cache usará la misma base de datos especicada en el archivo de conguración. Una vez que se hayas creado la tabla.26. revisión 789 del 9 de noviembre de 2009 . Memcached está libremente disponible en http://danga.py createcachetable [nombre_tabla_cache] Donde [nombre_tabla_cache] es el nombre de la tabla a crear.1.19. Primero.242. Todos los datos son guardados directamente en memoria.19. coloca la propiedad nombre_tabla es el nombre cache es mi_tabla_cache: CACHE_BACKEND como "db://nombre_tabla".19.26.172. Más claramente. 15.19.240:11211. el cual está disponible en http://www.26.26. coloca CACHE_BACKEND como memcached://ip:puerto/. Es usado por sitios como Slashdot y Wikipedia para reducir el acceso a bases de datos e incrementar el rendimiento dramáticamente. Memcached está corriendo en localhost (127.172. CACHE 15.26. Después de haber instalado Memcached. En el siguiente ejemplo.26. es necesario que instales los bindings Python para Memcached.com/Community/software/python-memcached/. CACHE_BACKEND = 'memcached://172.19.1) en el puerto 11211: CACHE_BACKEND = 'memcached://127.164 CAPÍTULO 15. y el programa seguirá tratando el grupo de diferentes máquinas como una sola cache.242:11212.26. ambas en el puerto 11211: CACHE_BACKEND.0. donde ip es la dirección IP del demonio de Memcached y puerto es el puerto donde Memcached está corriendo.com/memcached/. Sin duda.19. obtener y eliminar arbitrariamente datos en la cache. Memcached es un framework de cache livejournal. originalmente desarrollado para manejar grandes cargas en LiveJournal (http://www. serán perdidos si los servidores se caen.1. Este nombre puede ser cualquiera que desees.

pero no es tan eciente como Memcache dada su estrategia de bloqueo simple y reserva de memoria.7. modicado convenientemente para que pueda ser usado por el sistema de archivos. apache.6. Tomará un valor de 300 si no se lo especica.1. /var/tmp/django_cache. si tu servidor corre como usuario exista y pueda ser leído y escrito por el usuario dos (pickled).1. Este argumento tomará el valor de 300 segundos (5 minutos) si no se lo especica. como /var/tmp/django_cache. usa de conguración para tu entorno de desarrollo. sólo implementa la interfaz de cache sin realizar ninguna acción. 15. Django incluye una cache dummy que no realiza cache. está disponible como 'simple:///'. y la cache de base de datos. ACTIVAR EL CACHE 165 15. coloca el tipo Por ejemplo. Asegúrate que el directorio apuntado por esta propiedad exista y que pueda ser leído y escrito por el usuario del sistema usado por tu servidor Web para ejecutarse. pero tu entorno de producción si lo hará. asegúrate que el directorio /var/tmp/django Cada valor de la cache será almacenado como un archivo separado conteniendo los datos de la cache serializa- pickle. por ejemplo: CACHE_BACKEND como 'dummy:///' en el archivo CACHE_BACKEND = 'dummy:///' Como resultado de esto. Esta cache es por proceso y thread-safe. por ejemplo: CACHE_BACKEND = 'simple:///' Esta cache apenas guarda los datos en proceso.1. que usará la cache.1. coloca CACHE_BACKEND como 'locmem:///'. Continuando con el ejemplo anterior. y de un solo proceso en memoria. en segundos. lo que signica que sólo debe ser usada para desarrollo o testing. Las primeras dos son para y la tercera es el primer caracter de la ruta del directorio. aquí:: file://c:/foo/bar. 15. Cache en Memoria local Si quieres la ventaja que otorga la velocidad de la cache en memoria pero no tienes la capacidad de correr Memcached. Cache Dummy (o estúpida) Finalmente. 15. especicando el directorio en tu sistema de archivos que debería almacenar los datos de la cache. la cache de memoria local.5. por ejemplo: CACHE_BACKEND = 'locmem:///' 15. revisión 789 del 9 de noviembre de 2009 . Estos son dados como una query-string en la propiedad Los argumentos válidos son: CACHE_BACKEND. Para usarla.1.1. puedes optar por el cache de memoria-local.4. En ese caso. coloca lo siguiente: CACHE_BACKEND = 'file:///var/tmp/django_cache' Observa que hay tres barras invertidas en el comienzo del ejemplo anterior. Cache en Sistema de Archivos Para almacenar la cache en el sistema de archivos. max_entries: Para la cache simple. para almacenar los datos de la cache en "file://" en la propiedad CACHE_BACKEND. Cada nombre de archivo es una clave de la cache. Esto es útil cuando tienes un sitio en producción que usa mucho cache en varias partes y en un entorno de desarrollo/prueba en cual no quieres hacer cache. letra correspondiente al disco después de una barra al nal de la misma. es el número máximo de entradas permitidas en la cache a partir del cual los valores más viejos serán eliminados. No importa si colocas apache. coloca la La ruta del directorio debe ser absoluta --debe comenzar con la raíz de tu sistema de archivos. tu entorno de desarrollo no usará cache. Argumentos de CACHE_BACKEND Cada tipo de cache puede recibir argumentos. Si estás en Windows. file://. Cache Simple (para desarrollo) Una cache simple. usando el módulo Python file://.3. timeout: El tiempo de vida por omisión.15.

la cache middleware no intentará obtener la versión en cache de la página. CACHE_MIDDLEWARE_ANONYMOUS_ONLY. 'django. Mira la sección  Orden de MIDDLE- WARE_CLASSES más adelante en este capítulo. AuthenticationMiddleware y que AuthenticationMiddleware aparezca antes CacheMiddleware en tus MIDDLEWARE_CLASSES Finalmente. Si denes esta caracterís- la cache middleware sólo colocará en cache pedidos anónimos (p. agrega las siguientes propiedades en el archivo de conguración de Django: CACHE_MIDDLEWARE_SECONDS: El tiempo en segundos que cada página será mantenida en la cache. 15. Esta es una manera simple y efectiva de deshabilitar la cache para cualquier página de algún usuario especíco.: pedidos hechos por un usuario no logueado). la misma instalación Django. CACHE cull_frequency: La proporción de entradas que serán sacricadas cuando la cantidad de max_entries 1/cull_frequency. la manera más simple de usar la cache es colocar en cache el sitio Una vez que hayas especicado entero. o pasa parámetros POST.2. como se especica CACHE_MIDDLEWARE_SECONDS. se ja en y en CACHE_BACKEND = "locmem:///?timeout=30&max_entries=400" Tanto los argumentos desconocidos asi como los valores inválidos de argumentos conocidos son ignorados silenciosamente. u otra cadena que sea La cache middleware coloca en cache cada página que no tenga parámetros GET o POST. como la interfaz de administración de Django. tica.middleware. Un valor de 0 para cull_frequency signica que toda la cache será limpiada cuando se llegue a una cantidad de entradas igual a max_entries. Cache-Control para otorgarle una vida máxima a la página. Usa una cadena vacía si no te interesa.CommonMiddleware'. Esta cache middleware admite otras característica. timeout timeout se ja en 60: 30 max_entries 400: CACHE_BACKEND = "locmem:///?timeout=60" En este ejemplo. La proporción real es más rápido pero al costo de perder más datos de la cache. y la denes como True. nota que CacheMiddleware automáticamente coloca unos pocos encabezados en cada HttpResponse: Coloca el encabezado Last-Modified con el valor actual de la fecha y hora cuando una página (aún no en cache) es requerida.cache. La cache por sitio CACHE_BACKEND. no uses URLs con cadena de consulta. Este argumento tomará un valor de 3 si no se especica. Para activar la cache por sitio solamente agrega como en el siguiente ejemplo: MIDDLEWARE_CLASSES.166 CAPÍTULO 15. Ten en cuenta que si usas deberás asegurarte que has activado de CACHE_MIDDLEWARE_ANONYMOUS_ONLY. Esto signica que cada página que no tenga parámetros GET o POST será puesta en cache por un cierto período de tiempo la primera vez que sean pedidas.CacheMiddleware' a la propiedad MIDDLEWARE_CLASSES = ( 'django. Luego.cache. si quieres sacricar la mitad de las entradas cuando se llegue a una cantidad de max_entries coloca cull_frequency=2. 'django. a menos que sea aceptable que tu aplicación no coloque en cache esas páginas. ) Nota El orden de MIDDLEWARE_CLASSES importa. Si intentas usar la cache por sitio ten esto en mente cuando diseñes tu aplicación. coloca esta propiedad como el nombre del sitio. CACHE_MIDDLEWARE_KEY_PREFIX: Si la cache es compartida a través de múltiples sitios usando única para la instancia de Django.common. revisión 789 del 9 de noviembre de 2009 .middleware.CacheMiddleware'. Esto hace que el proceso de limpieza de la cache sea mucho es alcanzada. En este ejemplo. por ejemplo.middleware.e. para prevenir colisiones. Coloca el encabezado Coloca el encabezado en Expires con el valor de la fecha y hora más el tiempo denido en CACHE_MIDDLEWARE_SECONDS. Esto signica que si un usuario pide una página y pasa parámetros GET en la cadena de consulta.

cache import cache_page def my_view(request. que es un wrapper de la función de la vista que altera su comportamiento para usar la cache.views. como la cache por sitio. (r'^foo/(\d{1. Este enfoque acopla tu vista con el sistema de cache. cache_page(my_view. Por ejemplo. el my_view() estará en cache unos 15 minutos. los siguientes pedidos a esa URL utilizarán la cache. puedes usar la sintaxis de un decorador.views.3. 60 * 15)).cache. Se aplica a cualquier vista que tu especiques. si tu URLconf urlpatterns = (''.. (toma nota de que lo hemos escrito como 60 * 15 para que sea entendible. Pero una vez que 15.views. o puede que quieras distribuir las vistas a gente que quiera usarlas sin que sean colocadas en la cache. lo cual no es lo ideal por varias razones.cache import cache_page urlpatterns = (''. ) Ahora la misma cosa con my_view envuelto con cache_page: from django. my_view).3.decorators. Aquí el URLconf como estaba antes: cache_page cuando hagas referencia a ella urlpatterns = (''.e. Esto tiene el mismo efecto que la cache por sitio (incluyendo la omisión de colocar en cache los pedidos con parámetros GET y POST). my_view).cache import cache_page @cache_page(60 * 15) def my_view(request.decorators. Especicar la cache por vista en URLconf Los ejemplos en la sección anterior tienen codicado modica la función my_view 18 que la vista se coloque en cache. param): # . (r'^foo/(\d{1. /foo/23/). Si múltiples URLs apuntan a la misma vista.2})/$'. En el ejemplo anterior.2})/$'. ) Si tomas este enfoque no olvides de importar cache_page dentro de tu URLconf. param): # .decorators. es indexada independientemente de la URL. /foo/23/ serán puestos en cache separadamente. en vez de aplicarse al sitio entero. from django. CACHE POR VISTA 167 15.2})/$'.1. ) los pedidos a /foo/1/ y a una misma URL es pedida (p. como es de esperar... si estás usando la versión 2. Haz esto usando un decorador. porque cache_page ahí mismo. 60 * 15) De otra manera. revisión 789 del 9 de noviembre de 2009 .views.4 o superior de Python. El siguiente ejemplo es equivalente al anterior: from django. cache_page recibe un único argumento: el tiempo de vida en segundos de la cache. Continuando con el ejemplo de se ve como: my_view. El decorador de cache por vista es llamado por ejemplo: cache_page y se encuentra en el módulo django. my_view = cache_page(my_view. cada URL será puesta en cache separadamente.3.15. (r'^foo/(\d{1. Cache por vista Una forma más granular de usar el framework de cache es colocar en cache la salida de las diferentes vistas..) La cache por vista. La solución para estos problemas es especicar la cache por vista en URLconf en vez de especicarla junto a las vistas mismas. puede que quieras reusar las funciones de la vista en otro sitio sin cache. 60 * 15 será evaluado como 900--que es igual a 15 minutos multiplicados por 60 segundos cada resultado de minuto. Hacer eso es muy fácil: simplemente envuelve la función de la vista con en URLconf.decorators.

core. diccionarios. si no se lo especica. 'hello. puedes eliminar keys explícitamente con para un objeto en particular: cache.cache. N. 'b': 2. Si el objeto no existe en la cache.T. world!'. 3) >>> cache. 'c']) {'a': 1.. world!' El argumento timeout_seconds es opcional y obtiene el valor del argumento timeout de CACHE_BACKEND. lo resultados de las cuales cambian en intervalos diferentes. la cual vive en el módulo django. y funciona de la misma manera si existe o no un valor en la cache. revisión 789 del 9 de noviembre de 2009 . por ejemplo. un inconveniente excesivo. value. no será incluida en el diccionario.get_many(). Para casos como este. 'c': 3} Finalmente.get('my_key') None >>> cache.set('my_key'. 'c'. Esta es una manera fácil de limpiar la cache >>> cache.get('my_key') 'hello. revisa la documentación de Python para más información acerca de serialización). Quizás. explicado cache. Puedes colocar en la cache cualquier objeto Python que pueda ser serializado de forma segura: strings. porque no querrás guardar en cache todo el resultado (ya que los resultados cambian frecuentemente). CACHE 15. 'b'. 'b': 2.get('some_unset_key') None Te recomendamos que no almacenes el valor literal None en la cache.get('my_key'. listas de objetos del modelo.delete(). o el sistema de cache no se puede alcanzar. 'has expired') 'has expired' Para obtener múltiples valores de la cache de una sola vez. La API de cache de bajo nivel Algunas veces. y demás.. 1) >>> cache. Django expone una simple API de cache de bajo nivel. Si al sistema de cache le es tocará la cache sólo una vez. En este caso.set('c'. Esto especica qué valor debe devolver si el objeto no existe en la cache: >>> cache. pero querrás guardar en cache los resultados que rara vez cambian. timeout_seconds) y get(key): >>> cache. 2) >>> cache.get() puede recibir un argumento por omisión. Puedes usar la API de cache de bajo nivel para almacenar los objetos en la cache con cualquier nivel de granularidad que te guste. 'c': 3} Si una key no existe o ha expirado. usa posible. get_many() devuelve un diccionario con todas las key que has pedido que existen en la cache y todavía no han expirado: >>> cache. 'd']) {'a': 1.get() devuelve anteriormente. 'b'. de hecho. no sería ideal usar la página entera en cache que la cache por sitio o por vista ofrecen.delete() no tiene un valor de retorno.168 CAPÍTULO 15.delete('a') cache.get_many(['a'.set('b'. get_many() cache.get_many(['a'. (La mayoría de los objetos comunes de Python pueden ser serializados. al contrario de tocar la cache por cada valor. cache. 30) >>> cache.4. colocar en cache una página entera no te hace ganar mucho y es. tu sitio incluye una vista cuyos resultados dependen de diversas consultas costosas. >>> cache.: pickling Aquí vemos como importar la API: >>> from django. None: # Wait 30 seconds for 'my_key' to expire.set('a'.core. Lo siguiente es una continuación del ejemplo anterior: >>> cache. porque no podrás distinguir entre tu valor None almacenado y el valor que devuelve la cache cuando no encuentra un objeto.cache import cache La interfaz básica es set(key.

15. sin siquiera hacer nuevamente contacto con la página web para ver si esta ha cambiado.3 syntax. como las cookies o las preferencias del lenguaje. usa el decorador Vary para indicarle a la cache que esa página vary_on_headers como sigue: from django. Usar el encabezado Vary El encabezado Vary dene cuales encabezados debería tener en cuenta un sistema de cache cuando construye claves de su cache. si tu pides una página de http://example. my_view = vary_on_headers(my_view. manejando todo lo que se reera a cache transparentemente. Tu sitio en Django puede colocarse detrás de un cache proxy. Afortunadamente. El contenido de muchas páginas Web pueden cambiar según la autenticación que se haya realizado u otras variables. //www.4+ decorator syntax. si el contenido de una página Web depende de las preferencias de lenguaje del usuario. se dice que la página varía según el lenguaje. el lenguaje.. y será pasado a tu aplicación sólo si es Tu navegador también pone páginas en un cache.vary import vary_on_headers # Python 2.5..com/. si esta página produce contenidos diferentes basándose en algunas cabeceras del request--como las cookies. Por ejemplo. Aquí hay algunos ejemplos de caches para upstream: Tu ISP puede tener en cache algunas páginas.. En este caso. y los sistemas basados en almacenar en cache según la URL pueden exponer datos incorrectos o delicados a diferentes visitantes de esas páginas. @vary_on_headers('User-Agent') def my_view(request): # . 'User-Agent') # Python 2. def my_view(request): # . Para hacer esto en Django. el contenido de la bandeja de entrada obviamente depende de que usuario esté logueado. y para que algunas páginas particulares no se coloquen en cache.5. Eso.5. Sin embargo.views.com no tienen idea que esto pasa. Los responsables de example. como Squid Web Proxy Cache (http:: que coloca en cache páginas para un mejor rendimiento.. el mecanismo de cache (como middleware) colocará en cache una versión distinta de la página para cada tipo de user-agent. revisión 789 del 9 de noviembre de 2009 . pero puede ser peligroso.com directamente. Si el ISP hace caching de tu sitio ciegamente.e. el sistema de cache de Django crea sus claves de cache usando la ruta que se ha requerido (p. el primer usuario que ingrese al sistema compartirá su bandeja de entrada. que está en cache. La cache de upstream es un gran benecio. Por ejemplo. tu ISP te enviará la página sin tener que acceder a example. o el navegador--necesitarás usar el encabezado depende de esas cosas. CACHES UPSTREAM 169 15. digamos que manejas un sistema de e-mail basado en Web.org/). denitivamente no es bueno. necesario. Estos son sistemas que colocan en cache páginas aún antes de que estas sean pedidas a tu sitio Web. En este caso. Si una página Web envía unos encabezados apropiados. Existen un número de encabezados HTTP que indican a las cache de upstream que diferencien sus contenidos de la cache dependiendo de algunas variables. Por omisión. Veremos algunos de estos encabezados en las secciones que siguen.: Esto signica que cada pedido a esa URL usará la misma versión de "/stories/2005/jun/23/bank_robbed/"). independientemente de las características del navegador del cliente.com y tu navegador. el protocolo HTTP provee una solución a este problema. tu navegador usará su copia de la cache local para los siguientes pedidos a esa página.1. con los demás usuarios del sistema. el ISP se coloca entre example.decorators.squid-cache. Pero existe otro tipo de cache que es muy importante para los desarrolladores web: la cache realizada por los upstream. cada pedido será controlado por el proxy antes que nada. 15. cache. Caches upstream Este capítulo se ha enfocado en la cache de tus propios datos.

decorators.patch_vary_headers como función de ayuda. lo que signica que cada combinación de una cookie y un navegador obtendrá su propio valor en cache. y una cookie con el valor foo=ham. response = render_to_response('template_name'. ['Cookie']) return response patch_vary_headers obtiene una instancia de HttpResponse como su primer argumento y una lista/tupla de nombres de encabezados.5. El usuario generalmente se enfrenta con dos tipos de cache: su propia cache de su navegador (una cache privada) y la cache de su proveedor (una cache pública).cache import cache_control @cache_control(private=True) def my_view(request): # . Por ejemplo. "User-Agent" es lo mismo Vary header. Esta función ja o añade por ejemplo: from django. Por lo que las aplicaciones Web necesitan una manera de indicarle a la cache cuales datos son privados y cuales son públicos. Por ejemplo. Este decorador se encarga de enviar los encabezados HTTP apropiados detrás de escena. CACHE La ventaja de usar el decorador como vary_on_headers en vez de jar manualmente el encabezado Vary (usando algo response['Vary'] = 'user-agent') es que el decorador agrega al encabezado Vary (el cual podría ya existir). context) patch_vary_headers(response.. @vary_on_headers('Cookie') def my_view(request): # . vary_on_headers no diferencia mayúsculas de minúsculas.. Esto genera un problema con datos sensibles--no quieres que. el número de tu cuenta bancaria sea almacenado en una cache pública. El encabezado que le pasas a que al "user-agent". Para hacer esto en Django usa el decorador de vista cache_control: from django.. sin diferenciar mayúsculas de minúsculas. Existen otras pocas maneras de controlar los parámetros de cache.170 CAPÍTULO 15.utils..... Esto le dice a la cache de upstream que diferencie ambos. Una cache pública es usada por múltiples usuarios y controlada por algunos otros. La solución es indicar que la copia en cache de una página es privada.utils. como su segundo argumento.2..cache import patch_vary_headers def my_view(request): # . vary_on_headers(): en vez de jarlo desde cero y potencialmente sobrescribir lo que ya había ahí. Puedes pasar múltiples encabezados a @vary_on_headers('User-Agent'. Otros Encabezados de cache Otro problema con la cache es la privacidad de los datos y donde deberían almacenarse los datos cuando se hace un vuelco de la cache.. un pedido con navegador valor foo=bar será considerada diferente a un pedido con el navegador Mozilla Mozilla y una cookie con el Como las variaciones con las cookies son tan comunes existe un decorador vistas son equivalentes: vary_on_cookie.. 'Cookie') def my_view(request): # .views. Las siguientes dos @vary_on_cookie def my_view(request): # . revisión 789 del 9 de noviembre de 2009 . 15. por ejemplo. HTTP permite a las aplicaciones hacer lo siguiente: Denir el tiempo máximo que una página debe estar en cache.cache. También puedes usar django.

views.) En Django. incluyenque agrega Si utilizas Coloca el porque el middleware de cache necesita conocer los encabezados por los cuales cambiar el almacenamiento en la cache. Si utilizas un valor cache_control..ConditionalGetMiddleware agrega soporte para navegadores modernos para condicionar respuestas GET basadas en los encabezados ETag y Las-Modified.middleware. cache_control cache_control para especicar estos parámetros de la cache. OTRAS OPTIMIZACIONES 171 Especicar si una cache debería comprobar siempre la existencia de nuevas versiones. (Algunas caches pueden entregar contenido aun si la página en el servidor ha cambiado.GZipMiddleware comprime las respuestas para todos los navegadores modernos.600 segundos: from django.cache import cache_control @cache_control(must_revalidate=True. middleware de caching ya ja el encabezado propio max-age con de max_age en el un valor de decorador valores del encabezado serán fusionados 15. Orden de MIDDLEWARE_CLASSES CacheMiddleware. el decorador tendrá precedencia. GZipMiddleware.gzip. ahorrando ancho de banda y tiempo de transferencia. do los siguientes: SessionMiddleware. En el siguiente le indica a la cache revalidarse en cada acceso y almacenar versiones en cache hasta 3.decorators. utiliza el decorador ejemplo.6. Cookie que agrega Accept-Encoding revisión 789 del 9 de noviembre de 2009 .org/Protocols/rfc2616/rfc2616-sec14. CacheMiddleware después de cualquier middleware que pueda agregar algo al encabezado Vary.7.w3. Nota El CACHE_MIDDLEWARE_SETTINGS.15. simplemente porque la copia en cache todavía no ha expirado.html#sec14. ciones: Otras optimizaciones Django incluye otras piezas de middleware que pueden ser de ayuda para optimizar el rendimiento de tus aplica- django. django. Aquí hay una lista completa: public=True private=True no_cache=True no_transform=True must_revalidate=True proxy_revalidate=True max_age=num_seconds s_maxage=num_seconds Tip Para una explicación de las directivas Cache-Control de HTTP. entregando unicamente el contenido de la cache cuando no hubiesen cambios. y los correctamente. lea las especicaciones en http://www. Cualquier directiva Cache-Control de HTTP válida es válida en cache_control().http.6. 15. es importante colocarlas en el lugar correcto dentro de la propiedad MIDDLEWARE_CLASSES.middleware.9.. max_age=3600) def my_view(request): .

¾Qué sigue? Django incluye un número de paquetes opcionales.172 CAPÍTULO 15. Existen una cantidad interesante de herramientas disponibles. Duplicate explicit target name: próximo capítulo. CACHE 15.: hard-coded revisión 789 del 9 de noviembre de 2009 .8. 18 N. Hemos cubierto algunos de los mismos: el sistema de administración (Capítulo 6) y el marco de sesiones/usuarios (Capítulo 11). del T. no querrás perderte ninguna de ellas. El `próximo capítulo`_ cubre el resto de los marcos de trabajos de la comunidad.

contrib pueden requerir a otros. dentro de la base de datos.contrib. o en algún otro lugar. no puede ser cubierta por completo para cuando se publique de este libro. en inglés Cross-Site Request Forgery (CSRF). Este capítulo cubre dicha colección de agregados. Esta aplicación está actualmente bajo un fuerte desarrollo. seguirías pudiendo usar las capacidades fundamentales de Django sin problemas. Chequea el sitio web de Django para obtener la última información sobre esta aplicación. Consulta el Capítulo 12.Capítulo 16 Otros sub-frameworks contribuidos Una de las varias fortalezas de Python. emplean esa regla de oro al decidir en dónde va a residir la nueva funcionalidad. Este framework es usado internamente por otras aplicaciones contrib. que implementan varios lenguajes de marcado conocidos. si en django. revisión 789 del 9 de noviembre de 2009 . pero algunos sub-paquetes django. pero otros consisten solamente django. Dichos desarrolladores pueden hallar más información sobre esta aplicación. Cuando los desarrolladores de Django agregan nueva funcionalidad al framework. el framework de sesiones de Django. Consulta los capítulos 6 y 18. redirects: sessions: un framework para administrar redirecciones. Consulta la sección titulada  Filtros de marcado más adelante. leyendo el código fuente que está en django/contrib/contenttypes/. Consulta el Capítulo 12. y está especialmente enfocada a los desarrolladores de Django muy avanzados. contenttypes: un framework para conectar tipos de contenido. viene con una amplia biblioteca de paquetes que puedes comenzar a usar inmediatamente.contrib. Consulta la sección titulada  Flatpages más adelante. es su losofía de baterías incluidas. Consulta la sección titulada  Protección contra CSRF más adelante.contrib. Cuando instalas Python. plano.contrib consiste de los siguientes paquetes: admin: auth: el sitio automático de administración. 16. el framework de autenticación de Django. Consulta la sección titulada  Redirects más adelante. útiles para darle un toque de humanidad a los datos. Algunos de los paquetes incluyen modelos (y por lo tanto requieren que instales sus tablas en tu base de datos). e incluye su propia biblioteca estándar de agregados útiles para las tareas comunes del desarrollo web. comments: una aplicación para comentarios. csrf: protección ante un ataque de falsicación de petición en sitios cruzados. Consulta la sección titulada  `Haciendo los datos más humanos`_ más adelante. La única característica común a todos los paquetes de django. Django trata de seguir esta losofía. La biblioteca estándar de Django django. y por lo tanto. un conjunto de ltros de plantillas Django.contrib es la siguiente: si borraras dicho paquete por completo. django. flatpages: humanize: markup: un framework para administrar contenido HTML simple. sin necesidad de descargar nada más. Dentro de cada sub-paquete hay una pieza La biblioteca estándar de Django vive en el paquete de aislada de funcionalidad para agregar. No hay grandes requerimientos para los tipos de funcionalidad que hay en de middleware o de etiquetas de plantillas (template tags ). un conjunto de ltros de plantillas de Django. en que cada modelo de Django in- stalado es un tipo de contenido aislado. Estas piezas no están necesariamente relacionadas.1.

e inmediatamente obtiene un correo electrónico que dice Gracias por su suscripción. por medio del sitio de administración de Django. Sería ineciente y redundante implementar el código del procesamiento de registros dos veces. así que comenzaremos mostrando algunos escenarios en donde sería útil usarlo. El resto de este capítulo entra en los detalles de cada paquete libro.com').2. 'www. y que un artículo esté asociado con uno o más sitios por una relación de muchos-a-muchos.com se enfoca en noticias.ljworld. Pero a veces los editores quieren publicar un artículo en ambos sitios. sigue estos pasos: 1.174 CAPÍTULO 16. Agrega 2.sites' a tu INSTALLED_APPS. La manera en que uses estos dos conceptos queda a tu criterio. Empleando objetos variables El framework sites te proporciona un lugar para que puedas almacenar el nombre (name) y el dominio (domain) name (ej. Consulta la próxima sección. y es redundante conservar múltiples copias de la misma nota en las bases de datos. 16.contrib que no ha sido cubierto aún en este 16. y desde el mismo proyecto de Django. Sites El sistema sites de Django es un framework genérico que te permite operar múltiples sitios web desde la misma base de datos. Site asociado y con este archivo de conguración en particular.com. el diario Lawrence Journal-World de Lawrence. .contrib. o por medio de la API de Python.com y nuevamente para Lawrence. proporciona la tabla de base de datos que hace que los artículos se puedan relacionar de esta forma. OTROS SUB-FRAMEWORKS CONTRIBUIDOS sitemaps: un framework para generara archivos de mapas de sitio XML. Para instalar la aplicación sites. Consulta el Capítulo 11.2. son operados por la misma organización de prensa. sites: un framework que te permite operar múltiples sitios web desde la misma base de datos. django. que les permite a los lectores registrarse para obtener noticaciones.2. ¾Una solución mejor? Que ambos sitios usen la misma base de datos de artículos. los sitios LJWorld. Pero la noticia Gracias por su suscripción debe ser distinta para cada sitio. Escenario 1: reuso de los datos en múltiples sitios Como explicamos en el Capítulo 1.com. Pero esto es ineciente para los productores del sitio.1. Modo de uso del framework sites django. 16. y pedirle a los productores que publiquen la misma nota dos veces: una para LJWorld.com y Lawrence. de cada sitio de tu proyecto. y con una única instalación de Django. Ejecuta el comando manage.com') y domain (ej. Consulta el Capítulo 11.py syncdb para instalar la tabla django_site en tu base de datos. Agrega uno o más objetos Site. Éste es un concepto abstracto. los valores del nombre y dominio del sitio. mientras que Lawrence.2.sites. 16. que funcionan gracias a Django. LJWorld.2. siguiendo convenciones simples. en RSS y en Atom. Es bastante básico: un lector se registra en un formulario web.  Sites. Escenario 2: alojamiento del nombre/dominio de tu sitio en un solo lugar Los dos sitios LJWorld. Sirve para asociar datos con uno o más sitios. así que los sitios usan el mismo código detrás de escena. SITE_ID especica el ID de la tiene los campos Sites más que un framework.3.contrib. El framework sites de Django. Crea un objeto Site para cada sitio/dominio que esté respaldado por este revisión 789 del 9 de noviembre de 2009 proyecto Django.com y Lawrence. tienen la funcionalidad de alertas por correo electrónico. syndication: un framework para generar documentos de sindicación (feeds ). podemos abstraer el agradecimiento para usar 'LJWorld. Toda la cosa se basa en dos conceptos simples: el modelo Site. Site. que se halla en domain la opción de conguración base de datos del objeto name.com se enfoca en el entretenimiento local.com. y puede ser difícil de entender. 3. lo que signica que puedes reutilizar estos valores de manera genérica. pero Django los usa de varios modos de manera automática. Kansas. 'django. La forma cabeza dura de resolver el problema sería usar una base de datos para cada sitio. es una serie de convenciones.

4. Reuso de los datos en múltiples sitios Para reusar los datos en múltiples sitios. esta función de vista es reusable porque chequea el sitio del artículo dinámicamente. Por ejemplo.db import models from django. Obtención del sitio actual desde las vistas A un nivel más bajo. como explicamos en el primer escenario.Model): headline = models.. si un artículo sólo se permite en un sitio.SITE_ID) except Article.CharField(maxlength=200) # .16.ForeignKey(Site) Este tiene los mismos benecios.com tiene un Lawrence.contrib.. SITES 175 4..sites.get(id=article_id. según cuál sea el valor de la opción SITE_ID. site = models. como se describe en la última sección. entonces la búsqueda de artículos se limita a aquellos en que la lista de sitios incluye LJWorld.2. Continuando con el modelo ejemplo.models import Site class Article(models. Por ejemplo: revisión 789 del 9 de noviembre de 2009 . ManyToManyField hacia Site en tus modelos. usando ForeignKey.models import Site class Article(models. Con eso en su lugar. puedes usar el framework sites en tus vistas de Django para hacer cosas particulares según el sitio en el cual la vista sea llamada. Este valor debería ser el ID de base de datos del objeto SITE_ID en cada uno de tus archivos de conguración (settings ).. Las capacidades del framework Sites Las siguientes secciones describen las cosas que puedes hacer con este framework. puedes asociar un modelo con el modelo Site en una relación muchos-a-uno. y que el de Si esta vista es llamada cuando el archivo de conguración de LJWorld.Model): headline = models.objects.CharField(maxlength=200) # . aquí mostramos cómo luciría una vista article_detail: Article del from django.ManyToManyField(Site) Esa es toda la infraestructura necesaria para asociar artículos con múltiples sitios en tu base de datos. SITE_ID asignado a 1.com lo tiene asignado a 2. digamos que el archivo de conguración de LJWorld. simplemente debes agregarle un campo muchos-a-muchos..sites.contrib. article_id): try: a = Article.DoesNotExist: raise Http404 # . Site para el sitio respaldado por el archivo 16.. Por ejemplo: from django.2. puedes reusar el mismo código de vista para múltiples sitios.com.com está activado. Dene la opción de conguración de conguración. Asociación de contenido con un solo sitio De manera similar. sites = models. puedes usar un modelo como este: from django.conf import settings def article_detail(request.db import models from django. sites__id=settings. Por ejemplo.

contrib.objects. se logra simplemente haciendo referencia a name y a domain del objeto Site actual. We appreciate it..com y Lawrence. revisión 789 del 9 de noviembre de 2009 .conf import settings def my_view(request): if settings. 'Thanks for your subscription. ejemplo es from django.contrib..com. OTROS SUB-FRAMEWORKS CONTRIBUIDOS from django.SITE_ID == 3: # Do something.objects.get(id=settings.domain == 'foo. # .contrib. current_site = Site. El siguiente tan usado. es chequear el dominio actual del sitio: from django. como explicamos en Escenario 2: alojamiento del nombre/dominio de tu sitio en un solo lugar. and subscribe the user. el sujeto es Gracias por suscribirse a las alertas de LJWorld.domain == 'foo.models import Site def my_view(request): current_site = Site.conf.conf import settings from django.models import Site def my_view(request): current_site = Site.com': # Do something else: # Do something else.SITE_ID) if current_site.com.domain.' % current_site.SITE_ID es get_current(). etc.sites. Una forma levemente más limpia de lograr lo mismo. en cambio.core.\n\n-The %s team. Por supuesto.name.name. Este fragmento de código usado para obtener el objeto que el administrador de modelos de equivalente al anterior: Site (Site. En LJWorld.get_current() if current_site.176 CAPÍTULO 16..com el correo electrónico tiene como sujeto la línea Gracias por suscribirse a las alertas de lawrence. Nota En este último ejemplo..sites.objects. también se aplica al cuerpo del correo electrónico.get_current() send_mail('Thanks for subscribing to %s alerts' % current_site. Obtención del dominio actual para ser mostrado Una forma DRY (acrónimo del inglés Don't Repeat Yourself. no hay necesidad de importar django.com. else: # Do something else.. en Lawrence.models import Site from django. Por ejemplo: from django.settings.com. no te repitas) de guardar el nombre del sitio y del dominio. es horrible meter en el código el ID del sitio de esa manera.mail import send_mail def register_for_newsletter(request): # Check form values.com': # Do something else: # Do something else. Este comportamiento especíco para cada sitio. [user_email]) # .objects) Site según el valor de tiene un método settings. 'editor@ %s' % current_site.sites. Continuando con nuestro ejemplo de LJWorld.

and subscribe the user.FileField(upload_to='/home/photos') photographer_name = models. está muy bien.com y el de Lawrence.objects. puedes usar el framework sites.get_current(). pero también es Site lo más posible.CharField(maxlength=100) pub_date = models.get_absolute_url() '/mymodel/objects/3/' >>> Site.Model): photo = models. etc.com .Manager() on_site = CurrentSiteManager() Con este modelo. SITES 177 de Django. Si los CurrentSiteManager Site juegan roles importantes en tu aplicación.objects. Una buena idea es explotar los objetos innecesarias.sites. estas dos sentencias son equivalentes: Photo.SITE_ID) Photo.. Por ejemplo: from django. message.sites.16.contrib.get_absolute_url()) 'http://example.get(id=3) >>> obj.managers import CurrentSiteManager class Photo(models.render(Context({})) message = loader.sites. Para hacerlo.domain 'example.com tienen distintos directorios de plantillas (TEMPLATE_DIRS).on_site.models import Site from django. pero Photo.txt'). para que no haya una complejidad y una redundancia Obtención del dominio actual para las URLs completas La convención de Django de usar get_absolute_url() para obtener las URLs de los objetos sin el dominio. el de LJWorld. puedes simplemente delegarlo al sistema de plantillas así: Una forma aún más exible (aunque un poco más pesada) de hacer lo mismo..mail import send_mail from django. agregándolo a tu modelo explícitamente.render(Context({})) send_mail(subject.get_template('alerts/message.. Pero en en algunos casos puedes querer mostrar la URL completa -. En otras palabras.con para un objeto. Es un administrador de modelos (consulta el Apéndice B) que ltra automáticamente sus consultas para incluir sólo los objetos asociados al Usa CurrentSiteManager Site actual.all() retorna todos los objetos Photo de la base de datos.all() revisión 789 del 9 de noviembre de 2009 . debes crear las plantillas más complejo.txt y message. Context def register_for_newsletter(request): # Check form values.domain.txt'). de acuerdo a la opción de conguración SITE_ID. retorna sólo los objetos Photo.get_template('alerts/subject. y todo -- >>> from django.com'.models import Site >>> obj = MyModel. En este caso.com' >>> 'http:// %s %s' % (Site. [user_email]) # .on_site.. Este es un ejemplo: http:// y el dominio. es usando el sistema de plantillas from django.com/mymodel/objects/3/' 16.objects. subject = loader.get_current().contrib.contrib.core. obj.. Como mencionamos anteriormente.com y LJWorld.2. # .2.DateField() site = models.ForeignKey(Site) objects = models. Asumiendo que Lawrence.db import models from django. eso te da más exibilidad.objects. 'do-not-reply@example.template import loader.filter(site=settings. considera el uso del útil CurrentSiteManager en tu modelo (o modelos).5. subject.all() Photo asociados con el sitio actual.objects.txt en ambos directorios de plantillas.

Model): photo = models. lo demuestra: ¾Cómo supo tu modelo tiene un campo from django. algunas partes de Django -. Django no creará automáticamente el manager objects = models.6.contrib. Además.auth. la vista le pasa el nombre del Site actual a la plantilla como django. su SITE_ID site es asignado al SITE_ID actual.sites. Flatpages A menudo tendrás una aplicación Web impulsada por bases de datos ya funcionando. El uso que hace Django del framework y Sites Si bien no es necesario que uses el framework sites. Manager normal (no especíco al sitio) en tu modelo.178 CAPÍTULO 16. antes de denir CurrentSiteManager. El modelo a continuación. Incluso si tu instalación de Django está alimentando a un solo sitio. y apuntar su ID en tu opción de conguración SITE_ID.usan el manager que haya sido denido primero en el modelo. En el framework comments. incluso si Como se explica en el Apéndice B. Nota Probablemente querrás tener un usas CurrentSiteManager. cada objeto redirect está asociado con un sitio en particular. tú especicas su atpage chequea el SITE_ID site. En el framework atpages (consulta la sección  Flatpages más adelante).el sitio de administración y las vistas genéricas -. porque Django toma ventaja de ello en algunos lugares. debes pasarlo explícitamente como el parámetro para CurrentSiteManager. que tiene un campo llamado publish_on.Manager() 16. En el framework syndication (consulta el Capítulo 11). Django lanzará un ValueError. las plantillas para tienen acceso automático a la variable objeto {{ site }}. sólo los comentarios del sitio actual son mostrados. OTROS SUB-FRAMEWORKS CONTRIBUIDOS CurrentSiteManager cuál campo de Photo era el Site? Por defecto busca un campo llamado site. porque entonces tienes que preocuparte de la conguración revisión 789 del 9 de noviembre de 2009 .login {{ site_name }}. cada comentario está asociado con un sitio en particular.views. es extremadamente recomendado. Si ForeignKey o un campo ManyToManyField llamado de otra forma que site. Sería posible usar un servidor Web estándar como por ejemplo Apache para servir esos archivos como archivos HTML planos. Cuando una página es creada.db import models from django. que es el objeto Site title y description dede el que representa al sitio actual. pero necesitarás agregar un par de páginas estáticas.Manager(). si denes un manager manual- mente.contrib. la conexión para proporcionar las URLs de los elementos usan el Site domain actual si no especicas un fully qualied domain. y el middleware de actual cuando se traen páginas para ser mostradas.ForeignKey(Site) objects = models.managers import CurrentSiteManager class Photo(models. Así que si quieres que el sitio de administración tenga acceso a todos los objetos (no sólo a los especícos al sitio actual). cada página es asociada con un sitio en particular.2. toma en cuenta el actual.3.CharField(maxlength=100) pub_date = models. Además. en tu modelo. tales como una página Acerca de o una página de Política de Privacidad.sites. Este es el uso que hace Django del framework sites : En el framework redirects (consulta la sección  Redirects más adelante). pon un objects = models.models import Site from django.FileField(upload_to='/home/photos') photographer_name = models. y cuando los comentarios son listados con la etiqueta de plantillas apropiada. En el framework authentication (consulta el Capítulo 12). Cuando Django busca un redirect.Manager() on_site = CurrentSiteManager('publish_on') Si intentas usar CurrentSiteManager y pasarle un nombre de campo que no existe. 16.DateField() publish_on = models. deberías tomarte unos segundos para crear el objeto site con tu domain name. Cuando un comentario es posteado. pero eso introduce un nivel extra de complejidad en tu aplicación.contrib.

py syncdb a tu vari- 3. Puedes comprobar este valor en tu plantilla y mostrar un formulario de comentario si es necesario.ManyToManyField(Site) Examinemos cada uno de los campos: url: La URL donde reside esta atpage. pende de able de conguración 2. para instalar las dos tables necesarias en tu base de La aplicación atpages crea dos tablas en tu base de datos: simplemente mantiene una correspondencia entre URLs y títulos más contenido de texto. Es tu responsabilidad visualizarlo en tu plantilla.BooleanField() sites = models.sites. La solución a este problema es la aplicación atpages de Django.1. una tabla muchos a muchos que asocia una atpage con uno o más sitios. Esta aplicación te permite manejar esas páginas aisladas mediante el sitio de administración de Django.BooleanField() template_name = models. Las atpages son identicadas por su URL y su sitio.html. El framework no usa esto para nada en especial. registration_required: sites: Indica si se requerirá registro para ver esta atpage. y puedes acceder a las atpages con la API de bases de datos estándar de Django. y te permite especicar plantillas para las mismas usando el sistema de plantillas de Django. Cuando creas una atpage. Detrás de escena usa modelos Django.middleware. denido en from django. consulta la sección  Sites). visualizarlo en tu plantilla.contrib. 16. de la misma manera que el resto de tus datos. /about/contact/). django_flatpag django_flatpage_sites es django/contrib/flatpages/models. el cual se trata en la sección  Sites en este capítulo. tienes que preparar el acceso para que tu equipo pueda editar esos archivos. Esto se integra con el framework de autenticación/usuarios de Django. El framework no usa esto para nada en especial. y no puedes sacar provecho del sistema de plantillas de Django para darle estilo a las páginas.models import Site class FlatPage(models. sigue estos pasos: 1. (/) inicial (por ej.flatpages.FlatpageFallbackMiddleware' MIDDLEWARE_CLASSES.sites. Los sitios en los cuales reside esta atpage.3.contrib. si no se indica o si esta plantilla no existe. el cual se trata en el Capítulo 12. la cual reside en el paquete django. junto con en cuál(es) sitio(s) está (para más información acerca de sitios. el framework usará la plantilla flatpages/default. La aplicación incluye un único modelo se ve así: django_flatpage y django_flatpage_sites. Agrega 'django. FLATPAGES 179 de Apache.flatpages' a tu INSTALLED_APPS.contrib.3.CharField(maxlength=70. Usar atpages Para instalar la aplicación atpages.flatpages dedjango. manage. Ejecuta el comando datos. El framework no usa esto para nada en especial.contrib. el HTML de la página).CharField(maxlength=200) content = models.contrib. blank=True) registration_required = models.contrib.TextField() enable_comments = models. excluyendo el nombre del dominio pero incluyendo la barra title: El título de la atpage. lo que signica que almacena las páginas en una base de datos. Es tu responsabilidad content: El contenido de la atpage (por ej.Model): url = models.flatpages.CharField(maxlength=100) title = models. template_name: El nombre de la plantilla a usarse para renderizar esta atpage. especicas con cual URL está asociada.py. django. enable_comments: Indica si deben activarse los comentarios e esta atpage. Esto se integra con el framework sites de Django. asique asegúrate de que ambos paquetes se encuentren en INSTALLED_APPS. revisión 789 del 9 de noviembre de 2009 . El mismo FlatPage.db import models from django.16. Agrega 'django. Es opcional.

crea un directorio flatpages que contenga un archivo default. este middleware verica como último recurso la base de datos de atpages en búsqueda de la URL que se ha requerido.get(url='/about/') <FlatPage: /about/ -.. Es tu responsabilidad el crear la plantilla flatpages/default.. Agregar.. template_name=''.add(Site.get(id=1)) >>> FlatPage. title='About'. puedes colocar el la lista. deberías ver una sección Flatpages en la página de índice de la aplicación admin. Este es un ejemplo de una plantilla flatpages/default. . FlatpageFallbackMiddleware modicando y eliminando atpages`_.models import FlatPage >>> from django. url='/about/'. OTROS SUB-FRAMEWORKS CONTRIBUIDOS Puedes crear atpages ya sea a través de la interfaz de administración de Django o a través de la API de base de datos de Django...flatpages. Le pasa a dicha plantilla una única variable de contexto: RequestContext para renderizar la plantilla. .3.html si la atpage no ha especicado una plantilla personalizada. Cada vez que cualquier aplicación Django lanza un error. MIDDLEWARE_CLASSES es FlatpageFallbackMiddleware cerca o en el nal de 16. se encarga de todo el trabajo..objects. las atpages son renderizadas vía la plantilla <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4. debido a que se trata de una opción de último recurso. el Nota flatpage.title }}</title> revisión 789 del 9 de noviembre de 2009 . por ejemplo: django/contrib/flatpages >>> from django. registration_required=False.. Vía la API Python Como ya se describió. modicar y eliminar atpages Puedes agregar.</p>'. Usa Si SITE_ID.. Nota también que el orden de relevante.html: Por omisión. . la cual es el objeto atpage.3. Para más información.html.0 Transitional//EN" "http://www.dtd"> <html> <head> <title>{{ flatpage.3. flatpages/default.w3. o es el objeto atpage..no para errores 500 (error en servidor) u otras respuestas de error.sites. examina la sección  `Agregando. Generalmente.180 CAPÍTULO 16.html.contrib. cambiar y eliminar atpages de dos maneras: Vía la interfaz de administración Si has activado la interfaz automática de administración de Django.html. .. Una vez que has creado atpages.... A las plantillas de atpages se les pasa una única variable de contexto: flatpage. Usar plantillas de atpages para cualquier atpage con el campo flatpages/default.models import Site >>> fp = FlatPage( .. Edita las atpages como lo harías con cualquier otro objeto en el sistema. FlatpageFallbackMiddleware no encuentra una coincidencia. la cual proceso de la petición continúa normalmente.sites. las atpages se representan mediante un modelo Django estándar que reside en Por lo tanto puede acceder a objetos atpage mediante la API de base de datos Django. ..org/TR/REC-html40/loose. enable_comments=False.2. .. pero puedes cambiar eso template_name en el objeto FlatPage. Especícamente busca una atpage con la URL en cuestión y con un identicador de sitio que coincida con la variable de conguración Si encuentra una coincidencia.About> 16. carga la plantilla de la atpage.save() >>> fp.contrib. Este middleware sólo se activa para errores 404 (página no encontrada) -. ) >>> fp.objects. content='<p>About this site. En tu directorio de plantillas.

Agregar. Especícamente busca un redirect con el y con un identicador de sitio que coincida con la variable de conguración SITE_ID old_path provisto SITE_ID. redirecciona la petición a new_path está vació. Usar el framework redirects Para instalar la aplicación redirects. /music/ a /sections/arts/music/. deberías ver una sección Redirects en la página de índice de la aplicación admin. los desarrolladores Web deberían hacer lo que esté en sus 16. Generalmente puedes colocar RedirectFallbackMiddleware Nota Si usas los middlewares redirect y atpages. y el framework sites. Redirects El framework redirects de Django te permite administrar las redirecciones con facilidad almacenándolos en una base de datos y tratándolos como cualquier otro objeto modelo de Django. (para más información acerca de new_path.1. Luego entonces realiza los siguientes pasos: Si encuentra una coincidencia y Si encuentra una coincidencia y new_path no está vacío. Agrega 2.content }} </body> </html> 16.4. de búsqueda con campos manage. se encarga de todo el trabajo. Notar que el orden de cerca del nal de la lista. Esto es útil cuando necesitas cambiar las cosas de lugar en tu sitio. Por ejemplo puedes usar el framework redirects para indicarle a Django Redirecciona cualquier petición de manos para evitar los enlaces rotos. Edita las redirecciones como lo harías con cualquier otro objeto en el sistema.4. sigue estos pasos: 1. RedirectFallbackMiddleware modicando y eliminando redirecciones`_. envía una cabecera HTTP 410 (Ausente) y una respuesta vacía (sin contenido).contrib. Cada vez que cualquier aplicación Django lanza un error 404.contrib. Para más información puedes leer la sección  `Agregando. Esta se trata sencillamente de una tabla site_id.RedirectFallbackMiddleware' MIDDLEWARE_CLASSES. este middleware verica como último recurso la base de datos de redirects en búsqueda de la URL que se ha requerido. modicar y eliminar redirecciones de dos maneras: Vía la interfaz de administración Si has activado la interfaz automática de administración de Django. manage. el procesamiento de la petición continúa normalmente.py syncdb crea una tabla django_redirect en tu base de datos.4. modicar y eliminar redirecciones Puedes agregar. Si no encuentra una coincidencia. consulta la sección  Sites). revisión 789 del 9 de noviembre de 2009 . MIDDLEWARE_CLASSES es relevante. Sugerimos congurar atpages antes que redirects (o sea colocar el middleware atpages antes que el middleware redirects) pero tu podrías decidir lo contrario. 16. old_path y new_path. Agrega 'django. debido a que se trata de una opción de último recurso.4.redirects' a tu INSTALLED_APPS. REDIRECTS 181 </head> <body> {{ flatpage.no en errores 500 o respuestas con otros códigos de estado. Puedes crear redirecciones tanto a través de la interfaz de administración como a través de la API de base de datos de Una vez que has creado redirecciones.py syncdb para instalar la única tabla necesaria a tu base de datos. a tu vari- able de conguración 3. El middleware sólo se activa ante errores 404 -.2.redirects.16. Ejecuta el comando 'django.middleware.. la clase Django. analiza cual de los dos (redirect o atpages) desearías sea ejecutado primero.

. este esquema también puede ser atacado mediante CSRF -.objects.com example.com/logout no desconectará a un usuario. malicioso puede coercerte a visitar la URL example.. Un ejemplo más complejo de CSRF GET.com/logout usando POST y enviar la variable Bueno. deberán enviar una petición a con el valor 'true'. por ejemplo: >>> from django.save() >>> Redirect.get(id=1).redirects.com/logout..get(old_path='/music/') <Redirect: /music/ ---> /sections/arts/music/> 16.. por lo tanto saca ventaja de su estado autenticado.py. Supongamos que example.contrib. new_path='/sections/arts/music/'.csrf provee protección contra Cross-site request forgery (CSRF) (falsicación de El paquete peticiones inter-sitio). ) >>> red. aun con dichas medidas extra de seguridad.com tenía parte de la culpa debido a que permitía que se pudiera solicitar <input type="hidden" name="confirm" value="true" /> Esto asegura que un simple POST a la URL POST confirm usuarios puedan desconectarse. Protección contra CSRF django. Este sitio proveedor de webmail tiene un botón example.5. <iframe> invisible y luego usar JavaScript para enviar dicho revisión 789 del 9 de noviembre de 2009 . Inicialmente esto puede ser un poco difícil de entender así que en esta sección recorreremos un par de ejemplos. Claramente. . Por lo tanto puedes acceder a los objetos redirect vía la API de base de datos de Django.la página maliciosa sólo necesita hacer un poquito más de trabajo. Es una práctica mucho mejor el requerir POST HTTP para cada petición que cambie el estado en el servidor. Adicionalmente.contrib.5.1. el sitio example. De manera que si estás conectado (logged in ) a tu cuenta de webmail del sitio example. ser desconectado de un sitio de webmail contra tu voluntad no es un incidente de seguridad aterrorizante.models import Site >>> red = Redirect( .com. 16. para que los example. .com.sites.esto es. 16. ocultar el mismo en un formulario en forma automática. CSRF.. la única acción que necesitas realizar para desconectarte visitar la página example. también conocido como session riding (montado de sesiones) es un exploit de seguridad en sitios Web. Los atacantes pueden crear un formulario completo que envíe su petición a tu sitio.com ha mejorado su funcionalidad de desconexión de manera que Log Out es ahora un botón de un <form> que es enviado vía un POST a la URL example. old_path='/music/'. OTROS SUB-FRAMEWORKS CONTRIBUIDOS Vía la API Python Las redirecciones se representan mediante un modelo estándar Django que reside en django/contrib/redirects/models.com/logout -.models import Redirect >>> from django.5. Un ejemplo simple de CSRF Supongamos que posees una cuenta de webmail en Log Out que apunta a la URL (log out ) es Un sitio example.2.182 CAPÍTULO 16. pero este tipo de exploit puede sucederle a cualquier sitio que confía en sus usuarios.com/logout.. el hecho de visitar la misma te desconectará de oculto en su propia página maliciosa. example. tales como un sitio de un banco o un sitio de comercio electrónico.com/logout incluyendo esa URL como un <iframe> y visitas la página maliciosa.objects.. . Se presenta cuando un sitio Web malicioso induce a un usuario a cargar sin saberlo una URL desde un sitio al cual dicho usuario ya se ha autenticado. site=Site..contrib. Pero aun los sitios Web que requieren el uso de POST para acciones que signiquen cambios de estado son vulnerables a CSRF. el <form> de un cambio de estado (la desconexión del sitio) mediante el método HTTP el uso de un desconexión incluye un campo oculto: En el ejemplo anterior.

de manera que el costo en rendimiento es despreciable para peticiones que no usan sesiones. si un sitio malicioso incluye una de tus páginas como un <iframe>. tal como se explica en la siguiente sección. Si no cumple estas condiciones. Sólo modica las páginas que son servidas como text/html o Content-Type de la respuesta application/xml+xhtml. podrías estár salteandote el ltro que agrega el campo oculto al formulario. Si estás usando un framework de sesiones o autenticación personalizado que maneja en forma manual las cookies de sesión. y la expresión regular a veces no puede manejar código HTML muy extravagante). Por otra parte. ¾Cómo puede tu sitio defenderse de este exploit?. (Esto sucede porque document. este middleware no te será de ayuda. las peticiones asegurar eso. En el caso en el que estés interesado. revisión 789 del 9 de noviembre de 2009 .3. Este módulo contiene una clase midCsrfMiddleware la cual implementa la protección contra CSRF. antes de Limitaciones del middleware CSRF CsrfMiddleware necesita el framework de sesiones de Django para poder funcionar. cuando se esté realizando el procesamiento del formulario en el servidor. con el nombre csrfmiddlewaretoken y un valor que es un hash del iden- ticador de sesión más una clave secreta. Para más información y ejemplos sobre CSRF. debe procesar la respuesta antes que la misma sea comprimida o alterada de alguna otra forma.write). Entonces luego. Para activar esta proteccion. 2. si envía fragmentos de HTML en sentencias JavaScript expresión regular para De presentarse esta situación. Si tu aplicación crea páginas HTML y formularios con algún método inusual (por ej. Las peticiones Este middleware deliberadamente trabaja solamente sobre peticiones HTTP POST (y sus correspondientes formulaGET nunca deberían tener efectos colaterales.5. comprobar dicho campo secreto y generar un error si dicha comprobación no es exitosa. de manera que después de CsrfMiddleware debe aparecer GZipMiddleware. Usar el middleware CSRF django.CsrfMiddleware' a la variable de conguración MIDDLEWARE_CLASSES en tu archivo de conguración. es tu responsabilidad POST que no estén acompañadas de una cookie de sesión no son protegidas simplemente porque no tiene sentido protegerlas. Esto es precisamente lo que hace la capa de prevención de CSRF de Django. Como ya hemos explicado. sólo examina el código en tu navegador Web para ver si es que sido insertado en tu <form>. Request aborted. El middleware no modica la respuesta si no existe un identicador de sesión. PROTECCIÓN CONTRA CSRF 183 16. Este middleware necesita procesar la respuesta después de SessionMiddleware. Si sospechas que esto podría estar sucediendo.org/wiki/CSRF. agrega 'django. esto no tendrá un efecto negativo. el middleware revisa la cabecera modicarla. El primer paso es asegurarse que todas las peticiones no posean efectos colaterales. Para evitar alterar peticiones no HTML. el envío del formulario fallará siempre. Esto nos deja con las peticiones POST.wikipedia. Modica las respuestas salientes a peticiones agregando un campo de formulario oculto a todos los formularios POST. así es como trabaja 1. Una vez que has agregado eso a tu MIDDLEWARE_CLASSES ya estás listo. visita http://en.contrib. El contenido de la página de error es el mensaje Cross Site Request Forgery detected.csrf contiene sólo un módulo: middleware. Esto asegura que solamente se puedan usar formularios que se hayan originado en tu sitio Web para enviar datos vía POST al mismo. Previniendo la CSRF GET Entonces. (Revisa el Capítulo 12 para obtener más información sobre sesiones). rios POST). CsrfMiddleware usa una agregar el campo csrfmiddlewaretoken a tu HTML antes de que la página sea enviada al csrfmiddlewaretoken ha cliente. Realiza estas dos cosas:  Orden de MIDDLEWARE_CLASSES en el Capítulo 13 si necesitas conocer más sobre el tema. un sitio Web malicioso podría de todas formas generar ese tipo de peticiones.contrib. así que CsrfMiddleware debe aparecer antes que SessionMiddleware en la lista (esto es deEl paquete dleware Django: bido que el middleware de respuesta es procesado de atrás hacia adelante).16. el usuario recibirá un HTTP 403.py.csrf. comprueba que csrfmiddlewaretoken esté presente y tenga un valor correcto.5. De esa forma. Revisa la sección CsrfMiddleware. El segundo paso es dotar a cada <form> que se enviará vía POST un campo oculto cuyo valor sea secreto y sea generado en base al identicador de sesión del usuario. Para todas las peticiones error POST que porten la cookie de sesión.middleware.

3. 16. Caso contrario retorna el numeral. agrega que has hecho eso. Una vez { % load humanize %} en una plantilla. Hacer los datos más humanos django. Puedes pasarle ya sea un entero o una representación en cadena de un entero.6. Puedes pasarle ya sea un entero o una representación en cadena de un entero.humanize a tu variable de conguración INSTALLED_APPS. intword Este ltro convierte un entero grande a una representación amigable en texto. 10 se convierte en 10. 2 se convierte en dos.6.000). Esto cumple con el estilo Associated Press. 16. 450000 se convierte en 450. Puedes pasarle ya sea un entero o una representación en cadena de un entero.2 millón.6.2. Ejemplos: 1000000 se convierte en 1.000.000. revisión 789 del 9 de noviembre de 2009 . Funciona mejor con números mayores a un millón. Se admiten valores hasta un billardo (1.000.000.2 millardos. Ejemplos: 4500 se convierte en 4. 1200000000 se convierte en 1. intcomma Este ltro convierte un entero a una cadena conteniendo comas cada tres dígitos.6. 4500000 se convierte en 4. 16.500.000.184 CAPÍTULO 16. ordinal Este ltro convierte un entero a una cadena cuyo valor es su ordinal. OTROS SUB-FRAMEWORKS CONTRIBUIDOS 16. 3 se convierte en 3rd. usa siguientes secciones.6. 2 se convierte en 2nd.000. Para activar esos ltros. Puedes pasarle ya sea un entero o una representación en cadena de un entero. apnumber Para números entre 1 y 9. 1200000 se convierte en 1. 16.4. Ejemplos: 1 se convierte en uno.0 millón. y tendrás acceso a los ltros que se describen en las Esta aplicación aloja un conjunto de ltros de plantilla útiles a la hora de agregar un toque humano a los datos.500. Ejemplos: 1 se convierte en 1st.000.contrib. 45000 se convierte en 45. este ltro retorna la representación textual del número.1.

org/wiki/ReStructuredText) En cada caso el ltro espera el texto con formato de marcado como una cadena y retorna una cadena representando el texto con formato. 16.7. Filtros de marcado La siguiente colección de ltros de plantilla implementa lenguajes comunes de marcado: textile: Implementa Textile (http://en. El middleware es esencialmente código que se ejecuta antes y/o después de cada petición y puede modicar cada petición y respuesta a voluntad. Una { % load markup %} en una plantilla y tendrás acceso a dichos ltros.7. FILTROS DE MARCADO 185 16.contrib. usa examina el código fuente en django. etc. agrega vez que hayas hecho esto.content|textile }} Para activar estos ltros.org/wiki/Markdown) markdown: restructuredtext: Implementa ReStructured Text (http://en.markup a tu variable de conguración INSTALLED_APPS.16. A continuación trataremos el middleware incluido con Django y explicaremos cómo puedes crear el tuyo propio.wikipedia.wikipedia. Por ejemplo el ltro textile convierte texto marcado con formato Textile a HTML: { % load markup %} {{ object. ¾Qué sigue? Muchos de estos frameworks contribuidos (CSRF.) hacen su magia proveyendo una pieza de middleware. revisión 789 del 9 de noviembre de 2009 .wikipedia. Para más detalles django/contrib/markup/templatetags/markup.org/wiki/Textile_%28markup_language%29) Implementa Markdown (http://en.py. el sistema de autenticación.8.

OTROS SUB-FRAMEWORKS CONTRIBUIDOS revisión 789 del 9 de noviembre de 2009 .186 CAPÍTULO 16.

y así sucesivamente. Así que aquí está una pequeña parte de middleware que le permite a los sitios que se ejecutan detrás de un proxy ver la dirección IP correcta en X-Forwarded-For. Esto puede causar unas pequeñas complicaciones. Es un sistema de plug-in liviano y de bajo nivel capaz de alterar de forma global tanto la entrada como la salida de Django. y csrf del Capítulo 14 hacen su magia a 17. Cada componente middleware es responsable de hacer alguna función especíca. flatpages.")[0] request. # Take just the first one. Éste código puede necesitar modicar la petición antes de que la vista se encargue de ella. una de las cuales es que la IP remota de cada petición (request. miremos un ejemplo muy sencillo. no la IP real que realiza la petición.split(". que es un conjunto de acoples dentro del procesamiento de petición/respuesta de Django.META['REMOTE_ADDR'] = real_ip revisión 789 del 9 de noviembre de 2009 . redirects.Capítulo 17 Middleware En ocasiones. has visto middleware varias veces ya: Todas las herramientas de usuario y sesión que vimos en el Capítulo 12 son posibles gracias a unas request. y explica cómo puedes escribir tu propio middleware. posmodernistas).META["REMOTE_ADDR"]: class SetRemoteAddrFromForwardedFor(object): def process_request(self. Si estas leyendo este libro de forma lineal (disculpen.META['HTTP_X_FORWARDED_FOR'] except KeyError: pass else: # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. Todas las aplicaciones contribuidas través de componentes middleware. necesitarás ejecutar una pieza de código en todas las peticiones que maneja Django. dirección IP que realiza la petición. Antes de entrar en los aspectos formales de los que es esa API.user pequeñas piezas de middleware (más especícamente. con el valor real de la request. Qué es middleware Un componente middleware es simplemente una clase Python que se ajusta a una cierta API. request. Los balanceadores de carga manejan esto estableciendo una cabecera especial.META["REMOTE_IP"]) será la del balanceador de carga. Sitios de tráco alto a menudo necesitan implementar Django detrás de un proxy de balanceo de carga (mira el `Capítulo 20`_).session y La cache global del sitio discutida en el Capítulo 13 es solo una pieza de middleware que desvía la llamada a tu función de vista si la respuesta para esa vista ya fue almacenada en la cache.1. el middleware hace que estén disponibles para ti en las vistas. Tu puedes hacer esto con el framework middleware de Django. puede necesitar registrar información sobre la petición para propósitos de debugging. real_ip = real_ip. Este capítulo se sumerge más profundamente en qué es exactamente el middleware y cómo funciona. request): try: real_ip = request.

Utiliza Inicializar: __init__(self ) __init__() para realizar una conguración a nivel de sistema de una determinada clase middleware. pueden simplemente acceder a request. Django aplica el middleware en el orden que gura en recorre hacia abajo la lista hasta la vista.3. De hecho.core. None.MiddlewareNotUsed. Por razones de rendimiento. y en las fases de respuesta y excepción. ejecutando cualquier otro middleware y la vista apropiada. muchos de los ejemplos en los capítulos previos han requerido cierto middleware.1. 17. el valor de camente insertado en y eso funcionará si se usa un proxy o no. HttpRequest. Si Una razón común para implementar un método __init__() es para vericar si el middleware es en realidad nece__init__() emite django.http.middleware. Esto signica que sario. que esta pieza de middleware ya viene incorporada en Django. cada clase middleware activada es instanciada sólo una vez por proceso servidor. Tu podrías usar esta característica para vericar si existe una pieza de software que la clase middleware requiere. echemos un vistazo a todos los métodos disponibles que las clases middleware pueden denir.doc. Métodos de un Middleware Ahora que sabes qué es un middleware y cómo instalarlo. Django inmediatamente devolverá ése objeto revisión 789 del 9 de noviembre de 2009 HttpResponse. ya has visto varios ejemplos de instalación de middleware. 17. aplica el middleware en el orden inverso. 17. 'django. Si una clase middleware dene un método 17. Si devuelve un objeto HttpResponse.188 CAPÍTULO 17.py startproject: MIDDLEWARE_CLASSES = ( 'django.sessions. Para activar un componente middleware.2.antes de que Django haya analizado sintácticamente la URL para determinar cuál vista ejecutar.META['REMOTE_ADDR'].no para peticiones individuales.al iniciar el servidor -. éste no debe tomar argumentos más allá del estándar self. si tu la cual explicaremos en breve. Django decir. 'django. Django continuará procesando esta petición.CommonMiddleware'.AuthenticationMiddleware'.2.common.middleware. MIDDLEWARE X-Forwarded-For de todas las peticiones será automátirequest. Esto signica que tus aplicaciones Django no necesitan conocer si están detrás de un proxy de balanceo de carga o no.XViewMiddleware' ) Una instalación Django no requiere ningún middleware -. En las fases de petición y vista. es llamada sólo una vez -. entonces Django removerá el middleware __init__().La tupla quieres -.pero te recomendamos que actives CommonMiddleware.META['REMOTE_ADDR']. MIDDLEWARE_CLASSES por omisión creada por cada componente middleware se representa con un string: la ruta Python completa al nombre de la clase middleware. Si esto es instalado (mira la siguiente sección). Se le pasa el objeto a tu voluntad. es una necesidad tan común. o vericar si el servidor esta ejecutándose en modo debug.middleware. el cual puedes modicar process_request() Si devuelve debe retornar ya sea None o un objeto HttpResponse. .3.3.middleware. a continuación se muestra la manera de instalar middleware.exceptions. Esta ubicada y puedes leer más sobre ella en la siguiente sección. Mira la sección  Cómo procesa una petición Django: Detalles completos en el Capítulo 3 para un repaso de las fases. agregarlo a la tupla En MIDDLEWARE_CLASSES. Django no se encargará de llamar a cualquier otro middleware (de ningún tipo) o a la vista apropiada. Instalación de Middleware Si has leído este libro completamente hasta aquí.contrib. o cualquier otra situación similar. MIDDLEWARE_CLASSES en tu archivo de conguración. __init__() de la pila de middleware.middleware.auth. Django trata MIDDLEWARE_CLASSES como una especie de wrapper El orden es importante.contrib.SessionMiddleware'. en django. Es alrededor de la función de vista: en la petición MIDDLEWARE_CLASSES. MIDDLEWARE_CLASSES puede estar vacía. aquí se muestra la tupla django-admin. Pre-procesador de petición: process_request(self. 'django. y en la respuesta la recorre hacia arriba. request) Éste método es llamado tan pronto como la petición ha sido recibida -. Por ejemplo. Para completar.

La lectura de su código debería darte una buena idea de la potencia del middleware. como por ejemplo la compresión con gzip del HTML de la respuesta.1: Argumentos que se pasan a process_view() Argumento Explicación El objeto request view args kwargs Así como el método Si devuelve HttpRequest. los cuales pueden retornar debe retornar un objeto None. Si devuelve un objeto HttpResponse.3. exception) Éste método es llamado sólo si ocurre algún error y la vista emite una excepción sin capturar. response) Éste método es llamado después de que la función de vista es llamada y la respuesta generada. Nota Django trae incorporado una serie de clases middleware (que se discuten en la sección siguiente) que hacen de buenos ejemplos. o incluso tratar de recuperarse del error automáticamente. args. process_request(). Pos-procesador de excepción: process_exception(self. También puedes encontrar una serie de ejemplos contribuidos por la comunidad en el wiki de Django: http://code. MÉTODOS DE UN MIDDLEWARE 189 17. Cuadro 17. Django usará esa respuesta en vez del manejador de excepción incorporado en el framework. un caso de uso obvio es la compresión de contenido. view. process_exception() debe retornar ya sea None o un objeto HttpResponse. el procesador puede modicar el contenido de una respuesta. pero antes de que ésa vista sea realmente ejecutada.17. request es el objeto petición. Si devuelve venido tratando hasta aquí. El diccionario de palabras clave argumento que será pasado a la vista. Django continuará procesando esta petición con el manejador de excepción incor- porado en el framework. 17. Los parámetros deben ser bastante auto-explicativos: respuesta retornados por la vista.djangoproject.3. Este es en realidad el objeto función en sí. process_view() debe retornar ya sea None o un objeto HttpResponse. y response es el objeto A diferencia de los pre-procesadores de petición y vista. request. Si devuelve un objeto HttpResponse. no el nombre de la función como string. Esa respuesta puede ser la respuesta original pasada a la función (posiblemente modicada) o una totalmente nueva. None. La función Python que Django llamará para manejar esta petición.5.4.3. Puedes usar este método para enviar noticaciones de error. La lista de argumentos posicionales que serán pasados a la vista. Django inmediatamente devolverá ése objeto HttpResponse. request. volcar información postmórtem a un registro. el cual es el objeto Exception real emitido por la función de vista. kwargs) Éste método es llamado después de la llamada al pre-procesador de petición y después de que Django haya determinado qué vista ejecutar.3.com/wiki/ContributedMiddleware revisión 789 del 9 de noviembre de 2009 . no incluye el argumento request (el cual es siempre el primer argumento de una vista). Aquí. Django no se encargará de llamar a cualquier otro middleware (de ningún tipo) o a la vista apropiada. Django continuará procesando esta petición. request. 17.3. Pre-procesador de vista: process_view(self. process_response() HttpResponse. y None. Pos-procesador de respuesta: process_response(self. ejecutando cualquier otro middleware y la vista apropiada. Los parámetros para esta función son el mismo objeto request con el que hemos exception. Los argumentos que se pasan a esta vista son mostrados en la Tabla 15-1.

middleware. serán redirigidas a la misma URL con el prejo www.middleware. La losofía es que cada URL debería existir en un -.2. pero si tu preeres lo contrario.4. Un motor de búsqueda indexador trataría de forma separada estas URLs.auth.AuthenticationMiddleware. Nosotros por lo general preferimos velocidad sobre ancho de banda. solo habilita este middleware. a Este middleware permite el soporte para autenticación. es igual a Realiza re-escritura de URL basado en las conguraciones APPEND_SLASH y PREPEND_WWW : Si APPEND_SLASH True.4.CommonMiddleware.190 CAPÍTULO 17. a menos que el último componente en el path contenga un punto. ya que DISALLOWED_USER_AGENTS requiere que sus valores sean expresiones regure. De esta manera foo. revisión 789 del 9 de noviembre de 2009 .4. django. por lo tanto es una buena práctica normalizar las URLs. que representa el usuario todo objeto HttpRequest que se recibe. que veremos en breve. por lo tanto es perfectamente adecuado incluir sentencias import en él.com/bar es distinta de example.compile()). MIDDLEWARE 17. El archivo de conguración es un archivo de Python. el cual maneja 17.com/bar es redirigido a foo.com/bar/. Ambas opciones tienen por objeto normalizar URLs. Esto puede reducir mucho la cantidad de ancho de banda que consume un servidor Web.GZipMiddleware. request. re.example. Este middleware comprime automáticamente el contenido para aquellos navegadores que comprenden la compresión gzip (todos los navegadores modernos). y se hará cargo de enviar respuestas Nota también que existe un middleware de ETags y hace algo más.com/bar/. esta conguración debería ser una lista de objetos de expresiones regulares compiladas que se comparan con el encabezado user-agent de cada petición que se recibe. el resultado de import re..middleware.common.compile(r'^Googlebot') ) Nota el común lares compiladas (es decir. Middleware de soporte para autenticación Clase middleware: actual registrado.gzip. Técnicamente la URL la cual a su vez es distinta de example. Si PREPEND_WWW es igual a True. 17. Aquí esta un pequeño ejemplo de un archivo de conguración: import re DISALLOWED_USER_AGENTS = ( re. Si página. Agrega el atributo Mira el Capítulo 12 para los detalles completos. los cuales discutiremos en las secciones que siguen. 17.user.3.com/bar/.com/bar/file.lugar. www. Middleware incluido Django viene con algunos middleware incorporados para lidiar con problemas comunes.1. las URLs que no poseen una barra al nal serán redirigidas a la misma URL con una barra al nal. Middleware Common Clase middleware: django.contrib. lo cual es perjudicial para la valoración de tu sitio en el motor de búsqueda. si es apropiado. GET condicional.y sólo un -. La desventaja es que esto toma un poco de tiempo de procesamiento para comprimir las páginas. pero foo. Middleware de compresión Clase middleware: django. Maneja ETags basado en la conguración USE_ETAGS : ETags son una optimización a nivel HTTP para almacenar condicionalmente las páginas en la caché.4. las URLs que no poseen el prejo www.compile(r'^OmniExplorer_Bot'). USE_ETAGS es igual a True. Django calculará una ETag para cada petición mediante la generación de un hash MD5 del contenido de la Not Modified. Este middleware agrega algunas conveniencias para los perfeccionistas: Prohíbe el acceso a los agentes de usuario especicados en la conguración DISALLOWED_USER_AGENTS : Si se especica.txt es pasado a través sin cambios.

middleware.ConditionalGetMiddleware. se emite un COMMIT. Esto es útil si estas 127. Solo usa este middleware cuando confíes absolutamente en el valor de HTTP_X_FORWARDED_FOR. django. X-View personalizadas a peticiones HEAD que provienen de direcciones IP denidas en la conguración INTERNAL_IPS.transaction.1.TransactionMiddleware.17.4.4.0. Si la respuesta contiene un encabezado Last-Modified o ETag. HEAD y ja los encabezados de respuesta Date 17.middleware.8.6. si este último esta proxy inverso que provoca que cada petición REMOTE_ADDR sea jada request. Middleware de GET condicional django. Middleware de cache de todo el sitio Clase middleware: 13.contrib. y la petición contiene If-None-Match o If-Modified-Since.9.XViewMiddleware.http.SetRemoteAddrFromForwardedFor. y ya que este establece REMOTE_ADDR basándose en HTTP_X_FORWARDED_FOR. signica que cualquiera Este middleware no hace validar Si no estas detrás de un proxy inverso que establece puede falsear su dirección IP. Middleware X-View django. Duplicate explicit target name: próximo capítulo.middleware.7. Clase middleware: Este middleware envía cabeceras HTTP 17. Si una vista con éxito. Cualquiera puede inventar el valor de HTTP_X_FORWARDED_FOR. vamos a cubrir el modo de integrarse con sistemas existentes.META['RE jado.0.4. revisión 789 del 9 de noviembre de 2009 . Middleware de transacción Clase middleware: de función se ejecuta Este middleware asocia un django. Los módulos middleware que se ejecutan fuera de este. El soporte para ETag depende de la conguración USE_ETAGS y espera que el encabezado ETag de la respuesta ya este previamente jado. 17. ¾QUÉ SIGUE? 191 17.SessionMiddleware.5.doc. Mira el Capítulo 12 para más detalles. COMMIT o ROLLBACK de la base de datos con una fase de petición/respuesta.http. 17. En el esquemas de bases de datos que has heredado de la década de los 80.4. Esto es usado por el sistema automático de documentación de Django. ¾Qué sigue? `próximo capítulo`_. Este se analizó en detalle en el Capítulo 17. Los módulos middleware que se ejecutan dentro de este (próximos al nal de la pila) estarán bajo el mismo control de transacción que las vistas de función. Atención! HTTP_X_FORWARDED_FOR.META['HTTP_X_FORWARDED_FOR']. la respuesta es reemplazada por una respuesta 304 (Not modied).el comportamiento por omisión de Django.5. no uses este middleware. Mira el Apéndice C para obtener más información sobre las transacciones de base de datos.4. se emite un ROLLBACK.middleware.sessions. GET condicionales. El orden de este middleware en la pila es importante. HTTP_X_FORWARDED_FOR automáticamente. y Content-Length También elimina el contenido de cualquier respuesta a una petición para todas las peticiones. 17. Este middleware almacena en la cache cada página impulsada por Django. se ejecutan con commit-on-save -. el encabezado ETag es Clase middleware: Este middleware provee soporte para operaciones jado por el middleware Common.4.middleware.5. Este middleware habilita el soporte para sesiones. Si la vista provoca una excepción. tales como Los desarrolladores Web y los diseñadores de esquemas de bases de datos no siempre tienen el lujo de comenzar desde cero. a Este es el ejemplo que examinamos en la sección anterior  Qué es middleware.4. Como se señaló anteriormente. Soporte para uso de proxy inverso (Middleware X-Forwarded-For) Clase middleware: django.middleware. Este establece el valor de basándose en el valor de parado detrás de un request.CacheMiddleware.cache. Middleware de soporte para sesiones Clase middleware: django.

MIDDLEWARE revisión 789 del 9 de noviembre de 2009 .192 CAPÍTULO 17.

1. 18. y DATABASE_NAME.Capítulo 18 Integración con Base de datos y Aplicaciones existentes Django es el más adecuado para el desarrollo denominado de campo verde -. Usaremos django-admin. Pero a pesar de que Django favorece a los proyectos iniciados desde cero. mysite/settings. provee las conguraciones de go cuáles son los parámetros de conexión a tu base de datos y cuál es su nombre. Edita el archivo de conguración en ese proyecto. Esta es una guía de un proceso típico de integración con una base de datos existente desde cero. DATABASE_USER.es decir. Ejecuta el comando de datos mirada python mysite/manage. Te daremos algunas sugerencias para esto en la siguiente sección. inspectdb. Especí- DATABASE_PASSWORD.py dentro de tu aplicación usando la redirección de salida 5. Edita el archivo mysite/myapp/models. Integración con una base de datos existente La capa de base de datos de Django genera esquemas SQL desde código Python -. es posible integrar el framework con bases de datos y aplicaciones existentes Este capítulo explica algunas de las estrategias de integración. Las únicas suposiciones son que Django esta instalado y tienes una base de datos existente. Mira el Capítulo 5 para más información).py startapp myapp (donde myapp es el nombre de tu aplicación). 18. comenzar proyectos desde cero.py inspectdb > mysite/myapp/models. Usaremos myapp como el nombre de aplicación python mysite/manage. Esta herramienta se llama manage. DATABASE_PORT. DATABASE_ENGINE.1.py inspectdb. 4.py 6.py. La utilidad inspectdb determina una representación del modelo que usará Django para cada una de tus tablas. necesitas crear modelos para tus tablas de la base de datos existente. para decirle a Djan- 2. Guarda la salida en el archivo estándar de la shell: python mysite/manage. Django incluye una herramienta que puede generar el código del modelo leyendo el diseño de las tablas de la base de datos. DATABASE_HOST. En tal caso.1. Crea una aplicación dentro de tu proyecto ejecutando aquí.py para limpiar los modelos generados y realiza cualquier personalización necesaria. (Ten en cuenta que algunas de estas conguraciones son opcionales. 3.pero con una base de datos existente. Para este propósito. revisión 789 del 9 de noviembre de 2009 . como si estuviéramos construyendo un edicio en un campo de verde pasto fresco. y puedes llamarla ejecutando el comando Empleo de inspectdb realiza una introspección de la base de datos a la que apunta tu archivo de conguración.py inspectdb. tú ya tienes los esquemas SQL.1. Esto examinará las tablas en la base DATABASE_NAME e imprimirá para cada tabla el modelo de clase generado. Hecha una a la salida para tener una idea de lo que puede hacer inspectdb. camente. Crea un proyecto Django ejecutando es el nombre de tu proyecto).py startproject mysite (donde mysite mysite como nombre de proyecto en este ejemplo. e imprime el código Python del modelo a la salida estándar. 19 . models.

Para manejar situaciones como ésta. Aquí hay algunos apuntes para lidiar con los modelos generados: 1. de la base de datos (ej. o puedes usar el sistema por omisión en conjunto con otros sistemas. querrás remover cualquier línea que se parezca a ésta: id = models. necesitarás insertar inspectdb detecta claves primarias para PostgreSQL. id. Mantén un ojo en eso. el sistema de autenticación de Django te permite conectarte con otras fuentes de autenticación. hay un mapeo de uno-a-uno entre las tablas de la base de datos y las clases del modelo). y necesitarás hacer una pequeña limpieza al código del modelo resultante... MySQL y SQLite. inspectdb db_column al nombre real Por ejemplo. el modelo generado tendrá un agregará for_field = models. En otros casos. si es necesario.1. Si un campo en tu base de datos no tiene un buen equivalente en Django. y cambia el tipo de campo adecuadamente si es necesario.194 CAPÍTULO 18. un tipo de campo del inspectdb no puede detectar AutoField. Si necesitas crear una relación en un modelo que todavía no esta denido. recuerda que Django agrega automáticamente un campo de clave primaria id si un modelo no tiene una clave primaria. Integración con un sistema de autenticación Es posible integrar Django con un sistema de autenticación existente -. ya que los modelos Django requieren tener un campo primary_key=True. inserta primary_key=True primary_key=True para al menos un campo en cada modelo. tal vez tengas que re-acomodar el orden de los modelos generados. revisión 789 del 9 de noviembre de 2009 . tu compañía ya puede tener una conguración LDAP que almacena un nombre de usuario y contraseña para cada empleado. usará TextField e insertará el comentario Python 'This field a continuación del campo en el modelo generado. La detección de claves foráneas sólo funciona con PostgreSQL y con ciertos tipos de tablas MySQL. Es decir. puedes usar el nombre del modelo. así que está en tí cambiar esto a 3. o for). los campos de clave foránea serán generados como campos asumiendo que la columna de clave foránea fue una columna INT. Por ejemplo. 6. el modelo Author debe Book. incluyendo campos de clave primaria Sin embargo. Limpiar los modelos generados Como podrías esperar.' a continuación del campo. INTEGRACIÓN CON BASE DE DATOS Y APLICACIONES EXISTENTES 18. DATE). IntegerField. '_field' al nombre del atributo y establecerá el atributo del campo (ej. Por lo tanto.otra fuente de nombres de usuario y contraseñas o métodos de autenticación. Si inspectdb no puede mapear un tipo de columna a modelo. en vez del objeto modelo en sí. El comando autoincrementado. DateField) es determinado mirando el tipo de la columna VARCHAR. La capa de modelo de Django no requiere que incluyas todos los campos de tu(s) tabla(s). 4.2. donde sea necesario.2. Cada modelo generado tiene un atributo para cada campo. tiene una columna INT llamada for. de manera que los modelos que hacen referencia a otros modelos estén ordenados apropiadamente. si un campo es type is a guess. pass. si una tabla campo como este: pass. con seguridad puedes dejarlo fuera. 7. Para otras bases de datos. Por ejemplo. Si tu base de datos contiene tablas que hacen referencia a otras tablas (como la mayoría de las bases de datos lo hacen).IntegerField(db_column='for') inspectdb insertará el comentario Python 'Field renamed because it was a Python reserved word. 2. Sería una molestia tanto para el administrador de red como para los usuarios.. si un modelo ser denido antes del modelo Book tiene una ForeignKey al modelo Author. sino que pueden causar problemas si tu aplicación agregara nuevos registros a estas tablas. Esto signica que tendrás que refactorizar los modelos para tablas con relaciones muchos-a-muchos en objetos ManyToManyField. Si un nombre de columna de tu base de datos es una palabra reservada de Python (como class o for). Cada tipo de campo (ej. 5. la introspección de la base de datos no es perfecta.' CharField. class.IntegerField(primary_key=True) No solo estas líneas son redundantes. 18. si cada uno de ellos tiene cuentas separadas en LDAP y en las aplicaciones basadas en Django. Cada tabla de la base de datos es convertida en una clase del modelo (es decir. Puedes anular el esquema por omisión de Django basado en base de datos.

models import User. get_user recibe un id -. debe ser una tupla de nombres de ruta Python que apuntan a clases que saben cómo autenticar. Si el primer método de autenticación falla. De cualquier manera puedes escribir un script para hacer esto por adelantado o tu método de autenticación puede hacerlo la primera vez que el usuario ingresa al sistema.ModelBackend'. coincide con esas credenciales.auth. un ID de la base de datos o cualquier cosa -.2.auth. password=None): # Check the username/password and return a User.backends. si las credenciales son válidas. username=None.) Ese es el esquema básico de autenticación que verica la base de datos de usuarios de Django. en tu directorio LDAP. Si no son válidas. AUTHENTICATION_BACKENDS se tiene en cuenta.contrib. como se muestra a continuación: class MyBackend(object): def authenticate(self.ADMIN_LOGIN == username) pwd_valid = check_password(password. Django detendrá el procesamiento en la primera coincidencia positiva. and a hash of the password. settings. Django intenta autenAUTHENTICATION_BACKENDS.18.y devuelve un objeto User. password=None): login_valid = (settings. por lo que si el mismo usuario y contraseña son válidos 18.1. Django mantiene una lista de back-ends de autenticación que utiliza para autenticar. Django intenta con el segundo. token=None): # Check the token and return a User. y debe retornar un objeto User que None. INTEGRACIÓN CON UN SISTEMA DE AUTENTIFICACIÓN 195 18. Cuando alguien llama a django. El método authenticate recibe credenciales como argumentos de palabras clave.2.contrib.conf import settings from django. y así sucesivamente. Use the login name. Escribir un back-end de autenticación get_user(id) y authenticate(**credentials). authenticate debe vericar las credenciales que recibe. Aquí está un ejemplo de back-end que autentica contra unas variables de usuario y contraseña denidas en tu archivo settings.). debe retornar usuario que existe en tu back-end (ej. For example: ADMIN_LOGIN = 'admin' ADMIN_PASSWORD = 'sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de' """ def authenticate(self.2.authenticate() (como se describió en el Capítulo 12). La lista de back-ends de autenticación a usar se especica en la conguración estar en cualquier lugar de tu ruta Python Por omisión.el cual podría ser un nombre de usuario. contiene lo siguiente: ('django.contrib. hasta que todos los back-ends han sido intentados. Ésta ticar usando todos sus back-ends de autenticación. Pero podría tambien autenticar un token.. etc. username=None.py y crea un objeto User de Django la primera vez que un usuario se autentica: from django. La mayoría de las veces se parece Un back-end de autenticación es un clase que implementa dos métodos: El método a esto: class MyBackend(object): def authenticate(self.2. La mejor manera de lidiar con esto es crear un objeto User de Django para cada De cualquier manera. check_password class SettingsBackend(object): """ Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD. Estas clases pueden AUTHENTICATION_BACKENDS 20 . El sistema de administración de Django esta altamente acoplado a su propio objeto User respaldado por base de datos descripto en el Capítulo 12.ADMIN_PASSWORD) if login_valid and pwd_valid: try: revisión 789 del 9 de noviembre de 2009 .auth. tu base de datos SQL externa. Especicar los back-ends de autenticación Detrás de escena. El orden de en múltiples back-ends.

user_id): try: return User.conf lo dice.is_superuser = True user. comenzando en la raíz. user = User(username=username. Para hacer esto. 18.py') user. para delegar pa- Es posible ejecutar una aplicación Django en el mismo servidor de una aplicación impulsada por otra tecnología. Nota que adjuntar Django a una URL calicada (como recortada /admin/ activarán Django. revisión 789 del 9 de noviembre de 2009 . /admin/ en el ejemplo de esta sección) no afecta a Django en /admin/people/person/add/). Integración con aplicaciones web existentes httpd. /people/person/add/).modpython SetEnv DJANGO_SETTINGS_MODULE mysite. trones de URL diferentes a distintas tecnologías (Nota que el `Capítulo 20`_ cubre el despliegue con Django en Apache/mod_python. Por ejemplo.is_staff = True user.settings PythonDebug On </Location> Con esto en su lugar.py will.DoesNotExist: # Create a new user.DoesNotExist: return None 18. El en dicha personalización. Django trabaja con la URL absoluta (ej.settings PythonDebug On </Location> Aquí. password='get from settings.196 CAPÍTULO 18.handlers. con Django. La clave está en que Django será activado para un patrón particular de URL sólo si tu archivo El despliegue por omisión explicado en el en un dominio particular: httpd. por lo tanto tal vez valga la pena leer ese capítulo primero antes de intentar esta integración). `Capítulo 20`_ asume que quieres que Django impulse todas las páginas <Location "/"> SetHandler python-program PythonHandler django. la línea <Location "/"> signica maneja cada URL.. no con una versión de la URL (ej. sólo congura la directiva <Location> <Location "/admin/"> SetHandler python-program PythonHandler django.handlers. Esta perfectamente bien limitar esta directiva administración de Django en a <Location> a cierto árbol de directorio. digamos que tienes una aplicación PHP existente que impulsa la mayoría de las páginas en un dominio y quieres instalar el sitio de /admin/: /admin/ sin afectar el código PHP.get(pk=user_id) except User.objects. Note that we can set password # to anything.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.objects. La manera más directa de hacer esto es usar el archivo de conguración de Apache.3.save() return user return None def get_user(self. ¾Qué sigue? `próximo capítulo`_ se enfoca Hablando del sitio de administración de Django y sobre cómo acomodar el framework para encajar con necesidades existentes. Cualquier otra página usará el análisis de las URLs. sólo las URLs que comiencen con cualquier infraestructura que ya exista.conf.core.get(username=username) except User.4. the password # from settings. because it won't be checked. INTEGRACIÓN CON BASE DE DATOS Y APLICACIONES EXISTENTES user = User.core. otra tarea común es personalizar el sitio de administración de Django. Esto signica que tu URLconf raíz debe incluir el prejo /admin/..

revisión 789 del 9 de noviembre de 2009 .18.: del inglés Python path.4. del T. ¾QUÉ SIGUE? 197 Duplicate explicit target name: próximo capítulo.: del inglés legacy databases and applications. del T. 20 N. aplicaciones y base de datos que ya están en uso en entornos de producción. 19 N.

198 CAPÍTULO 18. INTEGRACIÓN CON BASE DE DATOS Y APLICACIONES EXISTENTES revisión 789 del 9 de noviembre de 2009 .

Mientras el periodista ingresa datos a Django.org. y la mayoría de los desarrolladores que usan Django lo encuentran útil y eciente. más allá de estas tareas de entrada de datos obvias. es comun que los desarrolladores quieran personalizarlo o extenderlo. es útil poder entrar y editarlos fácilmente. Si tenemos cualquier tipo de tarea de introducción de datos. puesto que la mayoría de los datos provienen de una fuente automática. ese es el propósito detrás de esta caracteristica. Sospechamos que la gran mayoría de lectores de este libro tiene una horda de tareas de este tipo. El desarrollador diseña un modelo basado en esta información y luego abre la interfaz de administración para el periodista. después de todo. En el periódico donde Django fue creado orginalmente. revisión 789 del 9 de noviembre de 2009 . la interfaz de administración es una de las características más sobresalientes de este framework. Sin embargo. tener una una interfaz gráca al modelo revela problemas rápidamente. y ya es tiempo de volver atrás y dar una mirada más minuciosa al asunto. el programador puede enforcarse en desarrollar la interfaz accesible publicamente (½la parte divertida!). Antes de continuar con este capítulo. pongamos-. La interfaz de administración de Django brilla especialmente cuando usuarios no técnicos necesitan ser capaces de ingresar datos. asi como una forma fácil de remarcar la interfaz para que se indentique con tu sitio. Como dijimos varias veces antes. Las ultimas secciones del Capítulo 6 ofrecieron algunas maneras simples de personalizar ciertos aspectos de la interfaz.implicaba algo así: El periodista responsable del artículo se reune con uno de los desarrolladores y discuten sobre la información disponible. Debido a que esta interfaz es tan popular. encontramos que la interfaz de administración es útil en algunos otros casos: Inspeccionar modelos de datos : La primer cosa que hacemos cuando hemos denido un nuevo modelo es llamarlo desde la interfaz de administración e ingresar algunos datos de relleno. considera revisar ese material. el administrador es lo mejor que hay. es muy útil para modicar datos (se veía venir). cuando surgen problemas con los datos automáticos. lo reproduciremos nuevamente aquí: Obviamente. el desarrollo de una característica tipica online --un reporte especial sobre la calidad del agua del acueducto municipal. Gestión de datos adquiridos : Hay una pequeña entrada de datos asociada a un sitio como http://chicagocrime.Capítulo 19 Extender la Interfaz de Administración de Django El Capítulo 6 introdujo la interfaz de administración de Django. El capitulo 6 discute también cuando y porqué querrías usar la interfaz de administración y desde hicimos un gran salto desde esos párrafos hasta este punto. En otras palabras. la razón de ser de la interfaz de administración de Django es facilitar el trabajo simultáneo de productores de contenido y programadores. Esto es usual para encontrar errores de modelado. No obstante. cubre como personalizar las listas de cambio y los formularios de edición.

. El propósito primario de la interfaz de administración de Django es dejar que la gente edite información. confías. entonces tambien tienen permito editarlo..si conas en tus usuarios. Otra implicancia es que el sistema de permisos. 19. nota la ausencia de agregaciones en la interfaz.3. nadie necesita aprobar sus ediciones.y se espera que escribas tus vistas personalizadas para todo el resto. aunque poderoso. editando .. El Zen de la aplicación Admin En su núcleo. vayamos a una breve disgreción para una discusión losóca.1.. signica que Django asume que se puede conar que tus editores de contenido harán las cosas correctas. no existe la infraestructura necesaria para mostrar totales. así que vamos a cavar sobre el subtexto de esta frase en las secciones que siguen. es extremadamente simple -. Django asume que si la gente puede ver el contenido en la interfaz de administraión. Usuarios conables . De nuevo. no existe algo que permita forzar a que estos pasos se realicen en un determinado orden. pero de nuevo tiene poderosas y profundas repercusiones. mucha de esa misma losofía se sostiene (nota que extensibilidad no gura en nuestros objetivos). Si una tarea dada requiere una serie de pasos. el desarrollador. Si conas en que alguien edite sus propias historias. Debido a que vistas personalizadas pueden hacer cualquier cosa.1.200 CAPÍTULO 19. .4. Esto es. Hablaremos de estos casos para los que la interfaz de administración de Django no está diseñada un poquito más adelante. La intefaz de administración está diseñada para ser usada por usuarios en lo que tú. Cuando se va a extender la interfaz de administración.1. no permite (al momento en el que se escribe esto) limitar accesos basados en objetos especícos. EXTENDER LA INTERFAZ DE ADMINISTRACIÓN DE DJANGO La interfaz de administración de Django maneja estos casos comunes con algunas o ninguna personalización. nota la ausencia de un permiso puede ver (ve el Capítulo 12). Signica además que no hay procesos de aprobación para la edición de contenido -. Esto no signica sólo gente que ha sido autenticada. Por ejemplo. contenido estructurado Como el resto de Django. Sí.pero esa simplicidad se basa en un montón de asunciones importantes. la gestión unicada de todos estos casos signica que la interfaz no maneja igual de bien otros modos de edición. elegimos enfocarnos en una cosa y hacerla extremadamente bien. La interfaz se concentra en editar. Deberías tener en mente que la interfaz de administración es sólo una aplicación.1. como sucede con la mayoría de las generalizaciones en el diseño. no algo que se pueda implementar en código. y debido a que estas puede ser visualmente integradas a la interfaz de administración muy facilmente (como se describe en la siguiente sección). para cualquier otra cosa.1. Esto parece obvio al principio.. Esta supresión de un ujo de trabajo tambien proviene del principio de conanza: la losofía de la interfaz es que este ujo es una decisión personal. Otra cosa más importante es la ausencia de cualquier cosa que se aproxime remotamente a un ujo de trabajo.1. la interfaz de administración de Django está diseñada para una sola actividad: Usuarios conables editando contenido estructurado. pero primero. Aunque. Es enteramente posible revisión 789 del 9 de noviembre de 2009 . La entera losofía de la interfaz de administración de Django sigue directamente estas asunciones. conas en que ese usuario no edite las historias de cualquier otro sin permiso. 19.. como datos almacenados en archivos.. no está diseñada con este propósito en mente. Parada Completa A esta altura debería estar claro que la interfaz de administración de Django no intenta ser todas las cosas para toda la gente. las posibilidades de personalización incorporadas están un poco limitadas por diseño. aunque la interfaz es bastante útil para revisar datos (según se ha descripto). y aunque sea una muy compleja. necesitarás vistas propias.. promedios y esas cosas. 19. no hace nada que cualquier desarrollador Django con suciente tiempo no podría reproducir. Esto es porque sólo sirve para editar información almacenada en modelos de Django. la interfaz preere que trabajes con datos estructurados. 19.2. 19. Por caso. no en actividades alrededor de la edición. Finalmente. . y en cambio. la interfaz es para editar -.

Esta es una manera muy fácil de hacerlo: simplemente crea una plantilla llamada e inserta este código: admin/libreria/libro/change_form. preguntale a alguien de la comunidad Django si la rama newforms-admin ha sido integrada. querrás cambiar la plantilla sólo para un único objeto o aplicación (no globalmente). Cuadro 19. Para el momento en que leas esto. Finalmente. a la fecha que escribimos esto.html 19. Django cargará esas en vez de las vienen por defecto. Pesonalizar las plantillas de la interfaz Como sale de fábrica. Plantillas de modelos propios La mayoría de las veces querrás usar la primer plantilla para crear una basada destinada a un modelo especíco.html La mayoría de las veces. Estas plantillas globales se describen el la Tabla 17-1. los formularios de edición. Django provee un número de herramientas para personalizar las plantillas de la interfaz que vienen integradas.html Por ejemplo.html admin/<template>. 19. esas nuevas caracteristicas pudieron haberse incorporado a la distribución de Django ocial. Quizas algo parecido a lo que muestra la Figura 17-1. debemos destacar que. Pasado ese punto.html admin/books/change_form. PESONALIZAR LAS PLANTILLAS DE LA INTERFAZ 201 que en el futuro alguien desarrolle una interfaz de adminitración diferente que esté basada en un conjunto de asunciones distintas y que por lo tanto se comportará de otra manera. cada vista busca primero plantillas para modelos y aplicaciones especícas. sin embargo. más adelante en este capítulo.2. cualquier cosa que requiera un ujo de trabajo especíco o permisos granulares). miremos algunas maneras rápidas de modicar el aspecto (y en cierto grado.2. puedes reescribir la plantilla globalmente. Para ahora.las listas de cambio. supongamos que queremos agregar un pequeño texto de ayuda en la cabecera de nuestra página de libros.ht revisión 789 del 9 de noviembre de 2009 .html admin/<app_label>/<template>. Usualmente la mejor forma de realizar esto es extendiendo y agregando información a uno de los bloques denidos en la plantilla que se está modicando. por lo que si creas tus plantillas en alguno de los directorios declarados para tal n.2. las cuales veremos pronto.html admin/object_history. La vista de administración busca plantillas utilizando el mecanismo de carga de plantillas estándar. Para averiguar al respecto.1.html admin/change_form. necesitarás leer la sección titulada  `Creando vistas de administración personalizadas`_. el objetivo usualmente implica cambiar alguna de las plantillas para un item en particular. en el siguiente orden: admin/<app_label>/<object_name>/<template>. la vista del formulario de agregar/editar para un modelo en este orden: Libro en la aplicación libros busca plantillas admin/books/book/change_form. el comportamiento) de la interfaz de administración. El Capítulo 6 cubre algunas de las tareas más comunes: cambiar la marca de la interfaz de adminitración (para todos esos Jefes Pelopunta que odian el azul) y proveer un formulario de administración personalizado. pero para las tareas detrás de ellas (por ejemplo. Primero.1: Plantillas globales de la interfaz de administración Vista Lista de cambios Formulario para agregar/editar Conrmación de eliminación Historial de un objeto Nombre de la plantilla base admin/change_list. Por ejemplo.html admin/change_form. Así.html admin/delete_confirmation. las páginas de conrmación de eliminación y vistas de historial -. los desarrolladores de Django trabajaban en una nueva versión de la interfaz de administración que permite mucha más exibilidad y personalización. Cada vista de administración -.tienen una plantilla asociada que puede ser reescrita de diferentes maneras.19.

202 CAPÍTULO 19.1: Un formulario de edición de libros personalizado revisión 789 del 9 de noviembre de 2009 . EXTENDER LA INTERFAZ DE ADMINISTRACIÓN DE DJANGO Figura 19.

Esto es.</p> { % endblock %} Todas estas plantillas denen un número de bloques que puedes sobreescribir.contrib. los escuchamos llorar. Como con la mayoría de los programas.ejemplo.. nada de lo que la interfaz hace es especial de manera alguna -.. esto no podría ser más fácil. y conguraciones que inuyen en el comportamiento. ¾Pero como puedo cambiar la forma en que la interfaz de administración funciona ? La primer cosa para entender es que esto no es mágico. por lo que te animamos a mirar las plantillas originales (que se encuentran en django/contrib/admin/templates/) para trabajar con la información más actualizada. Un esqueleto del URLconf puede parecerse a algo así: from django. Primero. CREAR VISTAS DE ADMINISTRACIÓN PERSONALIZADAS 203 { % extends "admin/change_form.ya que se trata simplemente de un conjunto de vistas (que se encuentran en que manipulan datos como cualquier otra vista. </script> { % endblock %} Nota No estamos seguros porqué necesitarías jQuery en la página de historia de objetos. antes de la línea que incluye las vistas del administrador. { % block extrahead %}.html" %} { % block form_top %} <p>Insert meaningful help message here.conf. 19. es tan simple como esto: <head>.html" %} { % block extrahead %} <script src="http://media. Todo lo que han dicho es cómo cambiar la interfaz visualmente .19. actualicemos nuestro archivo URLconf. por supuesto.js" type="text/javascript"></script> <script type="text/javascript"> // code to actually use jQuery here.defaults import * urlpatterns = patterns(''.com/) en tu página de historia de objetos. 'mysite. Necesitamos insertar esta línea: django.books. hay bastante código allí.urls. Cada plantilla del administrador dene un cual puedes usar para incluir contenido extra dentro del elemento (http://jquery.posiblemennte para implementar algún widget especial o un comportamiento del lado del cliente. agreguemos una vista reporte de editores a nuestra aplicación de libros del Capítulo 6.report'). revisión 789 del 9 de noviembre de 2009 . No obstante. cuando te das cuenta que la intefaz de administración es sólo un juego de vistas.3. 19. A modo de ejemplo. JavaScript Personalizado Un uso común para estas plantillas propias para modelos implica agregar código JavaScript extra a las páginas de la interfáz -.. Construiremos una vista de administración que muestre la lista de libros en función de los editores -.admin_views.admin. pero.un ejemplo bastante típico de vista de reporte que puedes necesitar construir.2. este ejemplo es válido para cualquier plantilla de la interfaz de administración. incluir la biblioteca jQuery { % extends "admin/object_history.3. diferentes tipos de campos. Crear vistas de administración personalizadas Hasta ahora. Seguro. el Por ejemplo.2. Puedes usar esta técnica para incluir cualquier tipo de controladores JavaScript que puedas necesitar en tus formularios.views) (r'^admin/books/report/$'. Por suerte. la mejor documentación es el propio código..com/javascript/jquery. agregar las tuyas propias es más fácil de entender. y se debe a que tienen que lidear con todas las opciones. cualquiera que haya buscando agregar comportamientos personalizados a la interfaz de administración probablemente esté un poco frustrado.

y dejaremos que la plantilla maneje el agrupamiento con la etiqueta con este código: books/admin_views. Django encontrará una vista por omisión para ese patrón y no funcionará como queremos.urls')).py. books luego de nuestra se considera una buena practica para mantener todas tus plantillas de administración agrupadas en admin/. pero este decorador también verica que el usuario esté marcado como un mientro del sta . Esto login_required discutido en el Capítulo 12. `Capítulo 10`_ para saber más sobre Esto asegura que la información sobre el usuario en curso está disponible para la plantilla.template import RequestContext django. Extenderemos una plantilla de la administración para que lograr que nuestra vista coincida visualmente con el resto de la interfaz: { % extends "admin/base_site.models import Book django. y tenga en consecuencia acceso a la interfaz de administración Este decorador protege todos las vistas predenidas del administrador.decorators.decorators import staff_member_required def report(request): return render_to_response( "admin/books/report. También pusimos la plantilla en un directorio llamado aplicación -. from from from from { % regroup %}. y hace que la lógica de autenticación para tus vistas coincida con la del resto de la interfaz.contrib. ) report = staff_member_required(report) Debido a que dejamos el agrupamiento a la plantilla.204 CAPÍTULO 19.reporte').admin. esta vista es bastante simple. {'book_list' : Book. 'libreria. Usamos RequestContext como el tercer parámetro (context_instance) para render_to_response.html". Sin embargo.html" %} { % block title %}List of books by publisher{ % endblock %} { % block content %} <div id="content-main"> <h1>List of books by publisher:</h1> { % regroup book_list|dictsort:"publisher.objects.lo que es también una buena práctica. Aunque esto no es estrictamente requerido. Finalmente.name" by publisher as books_by_publisher %} { % for publisher in books_by_publisher %} <h3>{{ publisher. Renderizamos una plantilla que se encuntra bajo un directorio admin/. La inclusión de los patrones de urls del administrador coincide con casi cualquier cosa que llega a su punto de inclusión. que no existe.books.views. hay algunos fragmentos sutiles dignos de explicitar: Usamos el decorador es similar a staff_member_required de django. por lo que si invertimos el orden de esas lineas.list|dictsort:"title" %} <li>{{ book }}</li> revisión 789 del 9 de noviembre de 2009 . ¾Por qué ponemos la vista personalizada antes de incluir las del administrador? Recuerda que Django procesa los patrones de URL en orden. {}). haremos una plantilla para esta vista. (r'^admin/'. Para hacer honor a la simplicidad.shortcuts import render_to_response django.all()}. intentará cargar un un lista de cambios para un modelo Reporte en la aplicación libros. sólo cargaremos todos los libros dentro del contexto.admin. Mira el RequestContext. RequestContext(request. include('django.admin. Crea un archivo mysite. EXTENDER LA INTERFAZ DE ADMINISTRACIÓN DE DJANGO ) (r'^admin/libreria/reporte/$'.grouper }}</h3> <ul> { % for book in publisher.contrib. Ahora escribamos nuestra vista.views.contrib.admin_views. En este caso particular.

½Avanti! Duplicate explicit target name: próximo capítulo. la de edición o cualquier otra parte de la interfaz. 19.quizas no te hayas enterado de una las más fantásticas características de la interfaz de administracion: ½está disponible en casi 40 idiomas distintos! Esto es posible gracias al framework de internacionalización de Django (y el duro trabajo de los traductores voluntarios de Django). pero la parte importante esta partecita del URLconf: http://isbn. Puedes usar esta técnica para agregar cualquier cosa que sueñes para la interfaz de administración. Por ejemplo. conseguimos el look and feel de la interfaz de administración de Django gratis.add_by_isbn'). Sobreescribir vistas incorporadas Algunas veces las vistas de administración por omisión simplemente no te sirven.books. `próximo capítulo`_ explaya como usar este framework para crear sitios revisión 789 del 9 de noviembre de 2009 . La Figura 17-2 muestra como luce el resultado. El Django localizados.5. tu vista será invocada por sobre la de omisión. Es decir. simplemente haz que tu URL haga sombra sobre la incorporada.4. agregar_por_isbn reemplazará 19. 'mysite. El código para esa vista te lo dejamos como ejercicio.html. podriamos reemplazar la vista incorporada para crear libros con un formulario que permita a los usuarios ingresar simplemente un código ISBN.nu (r'^admin/bookstore/book/add/$'. Si esta linea aparece antes que las URLs de administracion en tu URLconf. Cerraremos este capítulo con algunas ideas para vistas de administración personalizadas. Podriamos seguir un truco similar para reemplazar la página de conrmación de eliminación. SOBREESCRIBIR VISTAS INCORPORADAS 205 { % endfor %} </ul> { % endfor %} </div> { % endblock %} Al extender admin/base_site.19. Luego podriamos buscar la información del libro desde y crear el objeto automáticamente. ¾Qué sigue? Si tu idioma nativo es el inglés --cosa que gracias a los traductores ya no es necesaria para leer este libro-. Fácilmente puedes reemplazarlas por las tuyas propias en cualquier etapa de la interfaz de administración.admin_views. la vista completamente a la vista estándar para ese modelo.4. si tu vista viene antes que la vista incorporada de la aplicación en URLconf. Recuerda que las llamadas vistas de administración personalizadas en realidad son sólo vistas comunes de Django. por lo que puedes usar todas las técnicas aprendidas en el resto de este libro para proveer una interfaz con tanta complejidad como necesites.

206 CAPÍTULO 19. EXTENDER LA INTERFAZ DE ADMINISTRACIÓN DE DJANGO Figura 19. revisión 789 del 9 de noviembre de 2009 .2: Una vista personalizada libros por editor.

gnu. Internacionalización se reere al proceso de diseño de programas para el uso potencial de cualquier *locale* Esto incluye el marcado del texto (tales como elementos de la interfaz con el usuario o mensajes de error) para su futura traducción. Localización se reere al proceso especíco de traducir un programa internacionalizado para su uso en un *locale* particular. Como la mayoría de los proyectos open source. Django también incluye más de 40 archivos de localización. Encontrarás a menudo localización abreviada como L10N. y existen variables de conguración que controlan la visualización de valores dependientes del probabilidad de que Django ya se encuentre traducido a tu idioma nativo. la abstracción de la visualización de fechas y horarios de manera que sea posible respetar diferentes estándares locales. Lawrence. Kansas. *locale* como fechas y horarios. Usa esta información para traducir las aplicaciones Web para usuarios particulares de acuerdo a sus preferencias de idioma. todas las cadenas están marcadas para su traducción. la provisión de lo necesario para admitir diferentes zonas horarias. Django hace dos cosas: Le permite a los desarrolladores y autores de plantillas especicar qué partes de sus aplicaciones deben ser traducibles. en el mejor de los casos. Los mismos le indican a Django Este texto debe ser traducido al idioma del usuario nal si existe una traducción a dicho idioma de ese texto. Si no hablas inglés en forma nativa. y en general el asegurarse de que el código no contenga ninguna suposición acerca de la ubicación de sus usuarios. Debido a que muchos desarrolladores tienen. Django se encarga de usar esos de idioma del usuario.org/software/ gettext incluido en Python. revisión 789 del 9 de noviembre de 2009 . se halla a menos de 40 millas del centro geográco de la porción continental de los Estados Unidos). El mismo framework de internacionalización usado para esas localizaciones está disponible para que lo uses en tu propio código y plantillas. una comprensión difusa de dichos términos vamos a denirlos brevemente. Esos *hooks* reciben el nombre de cadenas de traducción. existe una buena *hooks* para traducir las aplicaciones Web al vuelo de acuerdo a las preferencias Nota La maquinaria de traducción de Django usa gettext/) via el módulo estándar gettext de GNU (http://www. sin embargo.Capítulo 20 Internacionalización Django fue originalmente desarrollado exactamente en el medio de los Estados Unidos (literalmente. En resumen. Esencialmente. la internacionalización y la localización fueron tomando una importancia creciente. la comunidad de Django creció hasta incluir gente de todo el globo. necesitarás agregar una cantidad mínima de *hooks* a tu código Python y a tus plantillas. Django en si está totalmente internacionalizado. A medida que la comunidad fue tornándose más diversa. Encontrarás a menudo internacionalización abreviada como I18N (el número 18 se reere al número de letras omitidos entre la I inicial y la N nal).

") return HttpResponse(output) La función django. 'to'. Este ejemplo es idéntico a los dos anteriores: def my_view(request): words = ['Welcome'.gettext() es idéntica a _()." está marcado como una cadena de traducción: def my_view(request): output = _("Welcome to my site. La traducción funciona también sobre valores computados. 'my'. Adrian is my name. Probablemente querrás también eliminar variable de conguración 'django. INTERNACIONALIZACIÓN Si no necesitas usar internacionalización: Los en un pequeño False *hooks* de internacionalización de Django se encuentran activos por omisión.208 CAPÍTULO 20.") return HttpResponse(output) La mayoría de los desarrolladores preere usar _().') % {'name': n} return HttpResponse(output) Esta técnica permite que las traducciones especícas de cada idioma reordenen el texto de los marcadores de posición. 20. Esta función está disponible globalmente (o sea como un componente incluido). debido a que es más corta. Si USE_I18N tiene el valor False Django implementará de tu algunas optimizaciones de manera de no cargar la maquinaria de localización. Funciones estándar de traducción Las cadenas de traducción se especican usando la función En este ejemplo.' output = _(sentence) return HttpResponse(output) (algo a tener en cuenta cuando se usan variables o valores computados.utils. una traducción al inglés podría ser podría ser Me llamo Adrian. Este ejemplo es idéntico al anterior: from django. revisión 789 del 9 de noviembre de 2009 Por esta razón. especi- cados con la sintaxis estándar de interpolación de cadenas con nombres.py. el nombre de la función es el carácter guión bajo). dichas cadenas pueden aparecer en tu código Python y en tus plantillas. De nuevo. deberías establecer USE_I18N = en tu archivo de conguración.'] output = _(' '.context_processors. "Welcome to my site. Es tú responsabilidad marcar las cadenas traducibles. (Si. Si usas interpolación posicional las traducciones no serán capaces de reordenar el texto de los marcadores de posición.1.utils.translation. lo cual incurre *overhead*. lación posicional (por ejemplo %s o %d). esas cadenas. Si no utilizas internacionalización. el sistema sólo puede traducir cadenas sobre las que está al tanto. no es necesario que lo importes. 'site. mientras que una traducción al español con el marcador de posición (el nombre) ubicado a continuación del texto traducido y no antes del mismo. Especicando cadenas de traducción en código Python Las cadenas de traducción especican Este texto debería ser traducido.1. Por ejemplo. 20.i18n' TEMPLATE_CONTEXT_PROCESSORS. por ejemplo: def my_view(request. Trataremos Las cadenas que le make-messages más adelante). es que la utilidad de detección de cadenas de traducción de Django. este es otro ejemplo idéntico: def my_view(request): sentence = 'Welcome to my site. como se veía en los dos ejemplos previos. deberías usar interpolación de cadenas con nombres (por ejemplo %(name)s) en lugar de interpo- . pasas a _() o gettext() pueden contener make-messages.core.translation import gettext def my_view(request): output = gettext("Welcome to my site. el texto _().join(words)) return HttpResponse(output) La traducción funciona también sobre variables. n): output = _(' %(name)s is my name.1. no será capaz de encontrar marcadores de posición (por placeholders ).

Model): name = models. el valor es accedido en lugar de cuando se llama a la función django.gettext_noop() para marcar una cadena como una cadena de tra- ducción sin realmente traducirla en ese momento.translation import gettext_lazy as _ class MyThing(models. ESPECIFICANDO CADENAS DE TRADUCCIÓN EN CÓDIGO PYTHON 209 20.translation import gettext_lazy as _ class MyThing(models.3.CharField(help_text=_('This is the help text')) Usa siempre traducciones perezosas en modelos Django (de lo contrario no serán traducidos correctamente para cada usuario). Y es una buena idea agregar también traducciones de los nombres de campos y nombres de tablas.utils.utils. Marcando cadenas como no-op Usa la función django. Usa este enfoque si deseas tener cadenas constantes que deben ser almacenadas en el idioma original -.Model): name = models. count ) % {'count': count} return HttpResponse(page) ngettext tiene tres argumentos: la cadena de traducción singular. gettext_lazy puedes simplemente crear un alias La traducción en si misma se llevará a cabo cuando sea usada en un contexto de cadena. de from django. Esto signica escribir las opciones verbose_name y verbose_name_plural en forma explícita en la clase Meta: from django.translation import ngettext def hello_world(request.ngettext() para especicar mensajes que tienen formas singular y plural distintas. revisión 789 del 9 de noviembre de 2009 .Model): name = models.translation.utils.2.20.translation. Las cadenas así marcadas no son traducidas sino hasta el último momento que sea posible.gettext_lazy() para traducir cadenas en forma perezosa gettext_lazy().CharField(help_text=gettext_lazy('This is the help text')) En este ejemplo. haz lo siguiente: -.utils. 20.pero que deben ser traducidas en el último momento posible. Pluralización Usa la función django. 'there are %(count)d objects'. la cadena de traducción plural y el número de objetos (el cual es pasado a los idiomas de traducción como la variable count).utils.1.1. count): page = ngettext( 'there is %(count)d object'.4.translation.1.1. tal como el renderizado de una plantilla en el sitio de administración de Django. help_text=_('This is the help text')) class Meta: verbose_name = _('my thing') verbose_name_plural = _('mythings') 20. Traducción perezosa Usa la función Por ejemplo. gettext_lazy() almacena una referencia perezosa a la cadena -. por ejemplo: from django.translation import gettext_lazy class MyThing(models.utils. para marcar el atributo help_text de un campo como traducible.CharField(_('name').tales como cadenas en una base de datos -.cuando from django. Si no te gusta el nombre largo la siguiente forma: _ (guión bajo) para el mismo.utils.no el verdadero texto traducido. por ejemplo cuando la cadena es presentada al usuario.

Si el valor es True. En dichos casos basta con que uses la sintaxis de traducción. rodeadas de comillas simples o dobles. {{ LANGUAGE_BIDI }} es el sistema de escritura del idioma actual.necesitas asociar la expresión a una variable local que será la que se usará dentro del bloque de traducción: { % blocktrans with value|filter as myvar %} This will have {{ myvar }} inside.210 CAPÍTULO 20. se trata de un idioma derecha-a-izquierda (por ejemplo hebreo. cuando usas ltros de plantillas -. {{ LANGUAGE_CODE }} es el idioma preferido del usuario actual. alemán). expresado como una cadena (por ejemplo en-us). Si el valor es False. árabe). especica tanto la forma singular como la plural con la etiqueta dentro de { % blocktrans %} y { % endblocktrans %}. La etiqueta de plantilla { % trans %} marca una cadena para su traducción: <title>{ % trans "This is the title. revisión 789 del 9 de noviembre de 2009 . Si tu traducción requiere variables (marcadores de posición) puedes usar por ejemplo blocktrans %}: {% { % blocktrans %}This will have {{ value }} inside. { % plural %} la cual aparece por ejemplo: { % blocktrans count list|length as counter %} There is only one {{ name }} object. usa la opción noop: <title>{ % trans "value" noop %}</title> No es posible usar variables de plantilla en { % trans %} -. { % endblocktrans %} Internamente. francés. se trata de de un idioma izquierda-a-derecha (por ejemplo inglés. Para que tus plantillas puedan acceder a esas etiquetas coloca plantilla. Puedes también cargar los siguientes valores usando etiquetas de plantilla: {% {% {% {% load i18n %} get_current_language as LANGUAGE_CODE %} get_available_languages as LANGUAGES %} get_current_language_bidi as LANGUAGE_BIDI %} *hooks* de traducción que están disponibles en el interior de cualquier etiqueta de bloque de También existen plantilla que acepte cadenas constantes. INTERNACIONALIZACIÓN 20. por ejemplo: _() para especicar una cadena { % some_special_tag _("Page not found") value|yesno:_("yes.no") %} En este caso tanto la etiqueta como el ltro verán la cadena ya traducida (en otras palabras la cadena es traducida antes de ser pasada a las funciones de manejo de etiquetas). todas las traducciones en bloque y en línea usan las llamadas apropiadas a Cuando usas RequestContext gettext/ngettext. { % endblocktrans %} Si necesitas asociar más de una expresión dentro de una etiqueta blocktrans. de manera que no necesitan estar preparadas para manejar traducción.{ % endblocktrans %} Para traducir una expresión de plantilla -. { % plural %} There are {{ counter }} {{ name }} objects.solo están permitidas cadenas constantes. (ver `Capítulo 10`_).2.por ejemplo. Especicando cadenas de traducción en código de plantillas { % load i18n %} al principio de tu Las traducciones en las plantillas Django usan dos etiquetas de plantilla y una sintaxis ligeramente diferente a la del código Python. tus plantillas tienen acceso a tres variables especícas rela- cionadas con la traducción: {{ LANGUAGES }} es una lista de tuplas en las cuales el primer elemento es el código de idioma y el segundo es el nombre y escrito usando el mismo)." %}</title> Si solo deseas marcar un valor para traducción pero para traducción posterior. (Consulta la sección  Cómo descubre Django la preferencia de idioma para información adicional). separa las partes con and: { % blocktrans with book|title as book_t and author|title as author_t %} This is {{ book_t }} by {{ author_t }} { % endblocktrans %} Para pluralizar.

sino el que se halla referenciado por o que se encuentra en algún punto debajo de esa ruta.py creará archivos vacíos.py -l de en formato locale.py:23 msgid "Welcome to my site. la cual aparece en el código fuente. msgstr es donde colocas la traducción especíca a un idioma.3. cada mensaje incluye el nombre del archivo y el número de línea desde el cual la cadena de traducción fue extraída. CREANDO ARCHIVOS DE IDIOMA 211 20.20.") entonces make-messages.po es sencillo." msgstr "" Es necesaria una rápida explicación: msgid es la cadena de traducción.po que contendrá el siguiente fragmento -. El formato de los archivos .po contiene una pequeña cantidad de metadatos tales como la información de contacto de quiénes mantienen la traducción. ¾Sin gettext? Si no tienes instaladas las utilidades Si es ejecutado sobre el árbol de tu proyecto o tu aplicación.3. No la modiques. Los archivos de mensajes tiene una extensión Django incluye una herramienta. Si te encuentras ante esa situación debes o instalar dichas utilidades o simplemente copiar el archivo de mensajes de inglés (conf/locale/en/LC_MESSAGES/django. Para crear o actualizar un archivo de mensajes. revisión 789 del 9 de noviembre de 2009 .un mensaje: #: path/to/python/module. Cada archivo .mapeos simples entre las cadenas de traducción y las traducciones al idioma en cuestión propiamente dichas. Por ejemplo. pero el grueso del archivo es una lista de mensajes -. Crea (o actualiza) un archivo de mensajes en el directorio conf/locale/de/LC_MESSAGES/django. que automatiza la creación y el mantenimiento de dichos bin/make-messages. make-messages.po) y usar el mismo como un punto de partida. Echa un vistazo a los códigos de idioma en el directorio django/conf/locale/ para ver cuales son los idiomas actualmente donde $PYTHONPATH django (no una copia de trabajo de Subversion. Creando archivos de idioma Una vez que hayas etiquetado tus cadenas para su posterior traducción. Por ejemplo. necesitas escribir (u obtener) las traducciones propiamente dichas. hará lo mismo pero la ubicación del directorio locale es gettext.1. Asegúrate de que mantienes las comillas alrededor de tu Por conveniencia. si tu aplicación Django contiene una cadena de traducción para el texto Welcome to my site: _("Welcome to my site. el mismo es incluidos. 20. La primera vez que lo ejecutes en tu árbol necesitarás crear el directorio locale. ejecuta este comando: bin/make-messages. En esta sección explicaremos como es que eso funciona. Creando los archivos de mensajes El primer paso es crear un archivo de mensajes para un nuevo idioma. Un archivo de mensajes es un archivo de texto común que representa un único idioma que contiene todas las cadenas de traducción disponibles y cómo deben ser representadas las mismas en el idioma en cuestión. En el ejemplo de. conf/locale. de manera que es tu responsabilidad el cambiar esto. El directorio raíz de tu proyecto Django El directorio raíz de tu aplicación Django El script recorre completamente el árbol en el cual es ejecutado y extrae todas las cadenas marcadas para traducción.po.3. . Su valor inicial es vacío traducción.py habrá creado un archivo .po. El código de idioma en este caso está pt_BR para portugués de Brasil y de_AT para alemán de Austria. el archivo será locale/LANG/LC_MESSAGES (nota que no tiene un prejo conf). se trata simplemente de un archivo de traducción vacío. El script debe ser ejecutado desde una de tres ubicaciones: El directorio raíz de es el código de idioma para el archivo de mensajes que deseas crear. archivos.

permite la selección del idioma basado en datos incluidos en la petición.4. UTF-8 debería funcionar para la mayoría de los idiomas gettext debería poder manejar cualquier conjunto de caracteres. Usa para ello la utilidad disponibles y crea archivos .3. o ambas. ½No olvides los espacios al nal de las cadenas. Si todo lo que deseas hacer es ejecutar Django con tu idioma nativo y hay disponible un archivo de idioma para el mismo.locale.middleware. Dichas cadenas se concatenan en forma directa. Django tiene un modelo muy exible para decidir qué idioma se usará -. los cuales son archivos binarios En el mismo directorio desde el cual ejecutaste make-messages. y cada vez que realices cambios sobre el mismo necesitarás compilarlo a una forma más eciente. LocaleMiddleware para cada usuario. 20.LocaleMiddleware a tu variable de conguMIDDLEWARE_CLASSES. a continuación vemos una traducción de múltiples líneas (extraída de la localización al español incluida con Django): msgid "" "There's been an error.necesitarás activar el sistema de traducción para tu aplicación.py -a 20. en caso contrario todas serán agrupadas sin espacios entre las mismas!. . Personaliza el contenido LocaleMiddleware.la opción a seleccionarse en último término si LANGUAGE_CODE. ejecuta lo siguiente: make-messages.po gettext. Debido a que el orden de los middlewares es relevante. Por ejemplo. Para congurar una preferencia de idioma a nivel de la instalación.mo." msgstr "" "Ha ocurrido un error. Para usar ración guías: Si deseas permitir que cada usuario individual especique el idioma que ella o él preere. ja ningún otro traductor encuentra una traducción. Django usará este idioma como la traducción por omisión -. Cómo descubre Django la preferencia de idioma Una vez que has preparado tus traducciones -. según los usa Esta herramienta recorre todos los archivos optimizados para su uso por parte de ejecuta compile-messages.determinado a nivel de la instalación. Se ha informado a los administradores del sitio " "mediante correo electrónico y debería arreglarse en breve. para un usuario particular.py Y eso es todo. revisión 789 del 9 de noviembre de 2009 . Generalmente.po con tu editor de texto favorito." Notar los espacios nales. de la siguiente manera: bin/compile-messages. usa LocaleMiddleware. bin/compile-messages. agrega django. simplemente asigna un valor a LANGUAGE_CODE en tu archivo de cong- uración.py gettext. primero edita la línea del conjunto de caracteres (busca por el texto pero "CHARSET") y ja su valor al del conjunto de caracteres usarás para editar el contenido. Detrás de escena. Compilando archivos de mensajes Luego de que has creado tu archivo de mensajes. Tus traducciones están listas para ser usadas.o. Thanks for your patience. Gracias por su " "paciencia. deberías seguir las siguientes Asegúrate de que se encuentre entre las primeras clases middleware instaladas. Ten en cuenta el conjunto de caracteres Cuando crees un archivo .py.212 CAPÍTULO 20.2. Para reexaminar todo el código fuente y las plantillas en búsqueda de nuevas cadenas de traducción y actualizar todos los archivos de mensajes para todos los idiomas. El contenido en si mismo se encontrará en las próximas líneas con el formato de una cadena por línea. La primera cadena inmediatamente a continuación de msgstr (o msgid) es una cadena vacía.py. si solo deseas usar las que están incluidas en Django -. INTERNACIONALIZACIÓN Los mensajes largos son un caso especial. It's been reported to the site administrators via e-" "mail and should be fixed shortly.

Un buen punto de partida es copiar el archivo también los mensajes de los validadores. Si deseas restringir la selección de idiomas a un subconjunto de los idiomas provistos (debido a que tu aplicación no incluye LANGUAGES a una lista de idiomas. el formato esperado para la preferencia de idioma es el formato estándar. LANGUAGES personalizado es posible marcar los idiomas como cadenas de traducción -. make-messages. La solución es usar una función gettext()` boba. querrás proveer al menos traducciones básicas para ese idioma. Django usa identicadores de mensajes técnicos para traducir formatos de fechas y de horas -. de-at (alemán Austríaco) pero Sólo pueden seleccionarse idiomas que se encuentren listados en la variable de conguración todos esos idiomas). Se eso falla. Si deseas ofrecer traducciones para tu aplicación que no se encuentran en el conjunto de traducciones incluidas en el código fuente de Django.transla desde el archivo de conguración debido a que ese módulo a su vez depende de las variables de conguración. como de-ch o en-us). Por ejemplo. Por ejemplo.middleware. _('German')). ('en'. CÓMO DESCUBRE DJANGO LA PREFERENCIA DE IDIOMA 213 Debe estar ubicado después de datos de la sesión. Si un idioma base está disponible pero el sub-idioma En cada uno de dichas ubicaciones.20.middleware. no la que se encuentra en django. _('English')).así que necesitarás al menos esas traducciones para que el sistema funcione correctamente.middleware. usa la variable de conguración global LANGUAGE_CODE. _('English')). Django usará el idioma base. 'django. y eso crearía una importación circular. portugués de Brasil es Django solo tiene disponible pt-br. Si eso falla. busca una cookie llamada Si eso falla. Esta cabecera es enviada por tu navegador Accept-Language. 'django. busca la cabecera HTTP django_language. busca una clave django_language en la sesión del usuario actual.SessionMiddleware'.LocaleMiddleware' ) LocaleMiddleware intenta determinar la preferencia de idioma del usuario siguiendo el siguiente algoritmo: Primero.contrib. ) Este ejemplo restringe los idiomas que se encuentran disponibles para su selección automática a alemán e inglés (y Si denes un cualquier sub-idioma.po de inglés y traducir al menos los mensajes técnicos.sessions. de manera que tendrás que recordar envolver los idiomas con la verdadera El gettext() en cualquier código que use LANGUAGES en tiempo de ejecución.pero usa una función gettext() boba. LocaleMiddleware esto es debido a que LocaleMiddleware usa CacheMiddleware.4. y quizá revisión 789 del 9 de noviembre de 2009 . usará de. Django intenta con cada idioma que aparezca en dicha cabecera hasta que encuentra uno para el que haya disponible una traducción. ja tu LANGUAGES. A continuación un archivo de conguración de ejemplo: _ = lambda s: s LANGUAGES = ( ('de'. si un usuario especica de .py todavía será capaz de encontrar y marcar dichas cadenas para su traducción pero la misma no ocurrirá en tiempo de ejecución. . Si usas SessionMiddleware. y le indica al servidor qué idioma(s) preeres en orden de prioridad. _('German')). Nunca debes importar django.translation.locale.utils. ('en'.utils. especicado no. LocaleMiddleware sólo puede seleccionar idiomas para los cuales exista una traducción base provista por Django. ) Con este esquema. coloca después de este (de otra forma los usuarios podrían recibir contenido cacheado del locale equivocado).common. como una cadena. Por ejemplo tu MIDDLEWARE_CLASSES podría verse como esta: MIDDLEWARE_CLASSES = ( 'django.CommonMiddleware'. Por ejemplo. por ejemplo: LANGUAGES = ( ('de'.

por ejemplo. guarda django_language. Finalmente. deber proporcionar la variante local correcta del valor provisto en inglés. busca un directorio locale en el directorio de la aplicación correspondiente a la vista que se locale en el directorio del proyecto. Si encuentra una traducción para el idioma seleccionado. Si encuentra una traducción.214 CAPÍTULO 20. request.LANGUAGE_CODE.urls.views. verica la traducción base en revisión 789 del 9 de noviembre de 2009 . Si la misma está vacía -. 20.set_language. GET. La vista de redirección set_language django. este sería la cadena de formato que deseas usar en tu idioma. Django incluye una vista de un usuario y redirecciona de vuelta a la página previa.i18n')). (Nota que este ejemplo publica la vista en La vista espera ser llamada vía el método el idioma en una cookie /i18n/setlang/). si el navegador de un usuario suprime dicha cabecera -.5. para eso sigue el siguiente algoritmo: Django busca un parámetro next en la cadena de consulta.") else: return HttpResponse("You prefer to read another language. con un parámetro language incluido en la cadena de consulta.LANGUAGE_CODE.conf. A continuación un ejemplo simple: def hello_world(request. El formato es idéntico al de la cadena de formato usado now. Django intenta la URL contenida en la cabecera Referer.1 }}</option> { % endfor %} </select> <input type="submit" value="Go" /> </form> 20. LocaleMiddleware ha determinado la preferencia del usuario. con por la etiqueta de plantillas Una vez que el DATETIME_FORMAT (o DATE_FORMAT o TIME_FORMAT). la deja disponible como request. Este es un fragmento de código de plantilla HTML de ejemplo: <form action="/i18n/setlang/" method="get"> <input name="next" type="hidden" value="/next/page/" /> <select name="language"> { % for lang in LANGUAGES %} <option value="{{ lang. Si el soporte para sesiones está activo.") Nota que con traducción estática (en otras palabras sin middleware) el idioma está en mientras que con traducción dinámica (con middleware) el mismo está en settings.i18n. include('django. Eres libre de leer este valor en tu código de vista. Si el mismo no existe o está vació. A continuación. INTERNACIONALIZACIÓN Los identicadores de mensajes técnicos son fácilmente reconocibles.0 }}">{{ lang. que ja la preferencia de idioma Por conveniencia. busca un directorio la misma será instalada.6.entonces el usuario será redireccionado a / (la raíz del sitio) como un último recurso. Después de haber jado la opción de idioma Django redirecciona al usuario. Activa esta vista agregando la siguiente línea a tu URLconf: (r'^i18n/'. Por ejemplo. Usando traducciones en tus propios proyectos Django busca traducciones siguiendo el siguiendo algoritmo: Primero. la misma será instalada. la vista guarda la opción de idioma en la sesión del usuario.LANGUAGE_CODE == 'de-at': return HttpResponse("You prefer to read Austrian German. está llamando. django/conf/locale. count): if request. No necesitas traducir los identicadores de mensajes como lo haces con otros mensajes.LANGUAGE_CODE para cada objeto petición. están completamente en mayúsculas. en cambio. Caso contrario.

puedes escribir aplicaciones que incluyan su propias traducciones. de acuerdo a lo que especiques ya sea en info_dict o en la URL. De esa forma make-messages.en el directorio en el cual exista ya sea el directorio conf/locale (en el caso del árbol de código fuente) o el directorio locale/ (en el caso de mensajes de aplicación o de proyecto).(po|mo) Todas las rutas listandas en orden en búsquda de LOCALE_PATHS en tu archivo de conguración <language>/LC_MESSAGES/django. .(po|mo) Para crear archivos de mensajes. debes dedicarle tiempo al diseño de la estructura de tus archivos de traducción. La forma de usar esto es asi: revisión 789 del 9 de noviembre de 2009 . 20. los mismos necesitan ser enviados desde gettext y demás desde JavaScript. Si tus aplicaciones necesitan ser enviadas a otros usuarios y serán usadas en otros proeyctos. y puedes reemplazar traducciones base colocando las tuyas propias en la ruta de tu proyecto. Solo necesitas estar en la ubicación adecuada -. Dichas cadenas de traducción se toman desde la aplicación.7. Finalmente. TRADUCCIONES Y JAVASCRIPT 215 De esta forma.20.7.py son examinadas en ese $PYTHONPATH/django/conf/locale/<language>/LC_MESSAGES/django.py para producir los archivos binarios django.1. Todos los repositorios de archivos de mensajes están estructurados de ka misma manera: $APPPATH/locale/<language>/LC_MESSAGES/django. Si no usas el middleware. Los catálogos de traducción para JavaScript deben ser mantenidos tan pequeños como sea posible.(po|mo) make-messages. La vista javascript_catalog La solución principal a esos problemas es la vista JavaScript con funciones que emulan la interfaz el javascript_catalog. 20. la salida más fácil de este problema es almacenar las aplicaciones que no son partes del proyecto (y por ende poseen sus propias traducciones) fuera del árbol del proyecto. Nota Si estás jando manualmente la variables de conguración. Pero el usar traducciones especícas a aplicaciones y aplicaciones en proyectos podrían producir problemas extraños con make-messages. usas la misma herramienta que usabas con los archivos de mensajes de Django. Usas también la misma herramienta compile-messages. el proyecto o el núcleo de Django.7. Traducciones y JavaScript El agregar traducciones a JavaScript plantea algunos problemas: El código JavaScript no tiene acceso a una implementación de El código JavaScript no tiene acceso a los archivos el servidor. solo serán procesados los archivos de mensajes de Django y del proyecto. (Django normalmente usa la ubicación del archivo de conguración para determinar esto. Es tu elección.py.(po|mo) $PROJECTPATH/locale/<language>/LC_MESSAGES/django.mo usados por gettext. posiblemente quieras usar traducciones especícas a dichas aplicaciones.po o .py ejecutado a nivel proyecto sólo traducirá cadenas que están conectadas a tu proyecto y no cadenas que son distribuidas en forma independiente. y en el caso que estés jando manualmente tus variables de conguración dicho archivo no existe).mo. make-messages recorrerá todos los directorios situados por debajo de la ruta actual y de esa forma podría colocar en el archivo de mensajes del proyecto identicadores de mensajes que ya se encuentran en los archivos de mensajes de la aplicación. el directorio locale en el directorio del proyecto no será examinado dado que Django pierde la capacidad de deducir la ubicación del directorio del proyecto. O puedes simplemente construir un proyecto grande a partir de varias aplicaciones y poner todas las traducciones en un gran archivo de mensajes. de manera que puedas llamar a gettext. que gettext más un arreglo de cadenas genera una biblioteca de código de traducción. Los archivos de mensajes de aplicaciones son un poquito complicados a la hora de buscar por los mismos -necesitas el LocaleMiddleware. Django provee una solución integrada para esos problemas: convierte las traducciones a JavaScript.

esto es útil si usas JavaScript que usa cadenas de diferentes aplicaciones.216 CAPÍTULO 20. Existe incluso una interfaz ngettext y una función de interpolación de cadenas: d = { count: 10 }. Esto no es tan rápido como la interpolación de cadenas en Python.views. La única diferencia es que es necesario que proveas un parámetro de la siguiente forma: make-messages. Puedes hacer que la vista sea dinámica colocando los paquetes en el patrón de la URL: urlpatterns = patterns(''. compile-messages. } urlpatterns = patterns(''.count). d.7.i18n.javascript_catalog'.conf 20. 11).py. de Django: con la herramienta make-messages. este cambia frecuentemente y no deseas tener que descargar un único gran catálogo. ngettext para generar 20. Como una medida de seguridad. ) Cada cadena en las cadenas en package debe seguir la sintaxis INSTALLED_APPS) y deben referirse paquete separados por puntos de Python (el mismo formato que a un paquete que contenga un directorio locale. 'django. La sintaxis de interpolación se tomó prestada de Python. s = interpolate(ngettext('this is %(count)s object'.views. Esto es especialmente útil si tus páginas usan código de diferentes aplicaciones.7.i18n. d).write(gettext('this is to be translated')). 'this are %s objects'. tu código JavaScript puede usar la interfaz estándar gettext para acceder al mismo: document. Una vez que se ha cargado el catálogo. (r'^jsi18n/(?P<packages>\S+?)/$. esos valores pueden solo tomar los valores django. (r'^jsi18n/$'. especicas los paquetes como una lista de nombres de paquetes delimitados por un símbolo + en la URL.py -d djangojs -l de Esto crea o actualiza el catálogo de traducción para JavaScript para alemán. 'this are %(count)s objects'.esto sigue siendo JavaScript así que el código tendrá que realizar múltiples sustituciones de expresiones regulares. Sin embargo.).app. De manera que el código de arriba podría haber sido escrito de la siguiente manera: s = interpolate(ngettext('this is %s object'.package'. todos esos catálogos son fusionados en un catálogo único. Usando el catálogo de traducciones JavaScript Para usar el catálogo simplemente descarga el script generado dinámicamente de la siguiente forma: <script type="text/javascript" src="/path/to/jsi18n/"></script> Esta es la forma en la que el sitio de administración obtiene el catálogo de traducciones desde el servidor.javascript_catalog').py de la misma manera que lo haces con los catálogos de traducción normales de revisión 789 del 9 de noviembre de 2009 . de manera que deberías reservarlo para los casos en los que realmente lo necesites (por ejemplo en combinación con pluralizaciones en forma correcta). sólo ejecuta Django. o cualquier paquete de la variable de conguración INSTALLED_APPS. INTERNACIONALIZACIÓN js_info_dict = { 'packages': ('your.2. Si especicas múltiples paquetes. Creando catálogos de traducciones JavaScript Los catálogos de traducciones se crean y actualizan de la misma manera que el resto de los catálogos de traducciones -d djangojs. La función interpolate admite tanto interpolación posicional como interpolación con nombres. 'django.3. no debes exagerar con el uso de la interpolación de cadenas -. Luego de haber actualizado catálogos. ) Con esto. js_info_dict). [11]).

El dominio posible. Usa *wrappers* Python alrededor de xgettext y Esto es más que nada por conveniencia. Django sólo usa cadenas /usr/share/locale/). DEFAULT_CHARSET. El `Capítulo 19`_ trata cómo puedes hacer para hace que tus sitios y tus usuarios estén seguros ante atacantes maliciosos y el `Capítulo 20`_ detalla cómo instalar una aplicación Django en uno o varios servidores. El dominio de cadenas se usa para diferenciar entre diferentes programas que almacenan sus datos en una biblioteca común de archivos de mensajes y plantillas y se carga en los catálogos de traducciones globales. revisión 789 del 9 de noviembre de 2009 .9. msgfmt.20. Deberías conocer lo suciente para comenzar a producir tus propios sitios usando Django. Usar ugettext Esto es debido a que Django siempre usa internamente no signicaría muchas ventajas ya que de todas formas siempre necesitarás producir UTF-8.8. Django no usa xgettext en forma independiente. Los siguientes dos capítulos cubren las cosas que necesitarás conocer si deseas que tu sitio sobreviva en el mundo real. NOTAS PARA USUARIOS FAMILIARIZADOS CON GETTEXT 217 20. escribir el código es solo el primer paso de la instalación de un sitio Web exitoso. Notas para usuarios familiarizados con gettext Si conoces gettext podrías notar las siguientes particularidades en la forma en que Django maneja las traducciones: django o El dominio de las cadenas es (usualmente djangojs. ¾Qué sigue? Este capítulo esencialmente concluye nuestra cobertura de las características de Django.8. 20. EL dominio django se usa para cadenas de traducción de Python djangojs se usa sólo para catálogos de traducciones de JavaScript para asegurar que los mismos sean tan pequeños como sea gettext y gettext_noop. Sin embargo.

218 CAPÍTULO 20. INTERNACIONALIZACIÓN revisión 789 del 9 de noviembre de 2009 .

Desafortunadamente. En cambio. y muchos. Para prevenir que los spammers lean todas las direcciones de email en nuestro sistema. El tema de la seguridad en la Web Si aprendes sólo una cosa de este capítulo. Debes convertir en una práctica general el preguntarte ¾De donde vienen estos datos? 21. cómo es que Django te protege. Django intenta mitigar esta dicultad. Por ejemplo. y otra información de petición). es importante entender de qué se tratan dichos problemas. sin embargo. muchos reportes de robos de identidad de sitios Web hackeados.vulnerabilidad existente. Esta vulnerabilidad se presenta más comúnmente cuando se está construyendo SQL a mano a partir de datos ingresados por el usuario.esto es lo más importante -. los papelones de seguridad con alta exposición pública parecen ser cosa de todos los días. Podría tratarse de uno de tus usuarios.confíes en datos enviados por un navegador. cookies.los pasos que puedes tomar para hacer tu código aun más seguro. Antes.y. vamos a exigir al usuario que escriba el nombre de usuario del cual quiere conocer sus datos antes de proveerle la dirección de email respectiva: revisión 789 del 9 de noviembre de 2009 . Es trivial falsicar los metadatos de la petición que los navegadores usualmente agregan automáticamente. desafortunadamente una de las más comunes -.los atacantes sólo necesitan encontrar una única vulnerabilidad. una interminable carrera armamentista contra los spammers. y tampoco trataremos de explicar cada vulnerabilidad en una forma completa. En estos tiempos. una importante aclaración: No es nuestra intención presentar una guía denitiva sobre todos los exploits de seguridad Web conocidos. Esto incluye tanto datos que se encuentran in band (por ejemplo enviados desde formularios Web) como out of band (por ejemplo cabeceras HTTP.Capítulo 21 Seguridad Internet puede ser un lugar aterrador. pero los defensores deben proteger todas y cada una. Está diseñado para protegerte automáticamente de muchos de los errores de seguridad comunes que cometen los nuevos (e incluso los experimentados) desarrolladores Web. Todo desarrollador Web necesita considerar la seguridad como un aspecto fundamental de la programación Web. Parte de las tareas de un desarrollador Web es hacer lo que esté en sus manos para combatir esas fuerzas de la oscuridad.1. Hemos visto virus propagarse con una velocidad asombrosa. presentaremos una breve sinopsis de problemas de seguridad que son relevantes para Django. pero con igual facilidad podría tratarse de un vil cracker buscando un resquicio. ejércitos de computadoras comprometidas ser empuñados como armas. Cualquier dato de cualquier naturaleza que arriba desde el navegador necesita ser tratado con una generosa dosis de paranoia. Todas las vulnerabilidades tratadas en este capítulo derivan directamente de conar en datos que arriban a través del cable y luego fallar a la hora de limpiar esos datos antes de usarlos. datos de Inyección de SQL GET/POST o URLs) para insertar fragmentos arbitrarios de SQL que una aplicación Web ingenua ejecuta La inyección de SQL es un exploit común en el cual un atacante altera los parámetros de la página (tales como directamente en su base de datos. y -. se da el caso de que implementar la seguridad es difícil -. Es probablemente la más peligrosa -. 21. Nunca sabes quién está del otro lado de esa conexión HTTP.2. que sea esto: Nunca -. Aun así. imaginemos que se escribe una función para obtener una lista de información de contacto desde una página de búsqueda.bajo ninguna circunstancia -.

. En ese SELECT * FROM user_contacts WHERE username = '' OR 'a' = 'a'. realmente lo es. Dicho parámetro acepta. nuestro intento de proteger nuestra lista de emails completa va a fallar con una consulta construida en forma ingeniosa. [user]) # . Consultas realizadas a mano usando la API de base de datos de nivel más bajo. la cláusula se retornen todas los registros.2. Escapa automáticamente todos los parámetros especiales SQL.GET['username'] sql = "SELECT * FROM user_contacts WHERE username = ' %s'. La API de base de datos de Django hace esto por ti. Sin embargo..db import connection def user_contacts(request): user = request.GET['username'] sql = "SELECT * FROM user_contacts WHERE username = %s. el ejemplo con el que comenzamos esta sección debe ser escrito de la siguiente manera: from django." % username # execute the SQL here. Por ejemplo. en esta llamada a la API: foo. la consulta que la interpolación construirá será: "'OR 'a'='a" en la caja de búsqueda. DELETE SELECT * FROM user_contacts WHERE username = ''.get_list(bar__exact="' OR 1=1") Django escapará la entrada apropiadamente. resultando en una sentencia como esta: SELECT * FROM foos WHERE bar = '\' OR 1=1' que es completamente inocua. En tales casos. hemos omitido deliberadamente la mayor parte del código necesario para hacer que el mismo realmente funcione." cursor = connection.cursor() cursor. ese es el menos pavoroso de los ataques. Esto se aplica a la totalidad de la API de base de datos de Django. por diseño.execute(sql. do something with the results revisión 789 del 9 de noviembre de 2009 . No queremos que este código sirva si accidentalmente alguien lo toma fuera de contexto y lo usa. Nota En este ejemplo. de acuerdo a las convenciones de uso de comillas del servidor de base de datos que estés usando (por ejemplo. Debido a que hemos permitido SQL sin protección en la string. A pesar de que a primera vista eso no parece peligroso. SQL parámetros asociados (bind parameters ). para ello evita realizar interpolación de strings y en cambio usa where del método extra() (ver Apéndice C). con un par de excepciones: El argumento crudo. PostgreSQL o MySQL)..1. Pensemos acerca de qué sucede si un atacante escribe caso. Imaginemos qué sucedería si el atacante envía Nos encontraríamos con la siguiente consulta completa: "'.. es fácil mantenerse protegido. Esto es. La solución Aunque este problema es insidioso y a veces difícil de detectar la solución es simple: nunca confíes en datos provistos por el usuario y siempre escapa el mismo cuando lo conviertes en SQL.220 CAPÍTULO 21. Primero. ½Ouch! ¾Donde iría a parar nuestra lista de contactos? 21. OR agregada por el atacante logra que FROM user_contacts WHERE 'a' = 'a'". SEGURIDAD def user_contacts(request): user = request. DELETE FROM user_contacts WHERE 'a' = 'a'. y en todos los ejemplos similares del tipo no hagas esto que siguen.

Consideremos esta simple vista Hola mundo: def say_hello(request): name = request. pero no olvides que este atacante había logrado que su código -. la página renderizada contendría lo <h1>Hello. Desafortunadamente.db. <i>Jacob</i>!</h1> Obviamente. django. {{ name }}!</h1> De manera que si accediéramos a siguiente: http://example. Jacob!</h1> Pero atención -. necesitas construir dinámicamente una lista de tablas a partir de una variable enviada mediante provee una función. {"name" : name}) Esta vista simplemente lee un nombre desde un parámetro GET y pasa dicho nombre a la plantilla hello. 21. Django la cual escapará el identicador de acuerdo al esquema de uso de comillas de la base de datos actual. La solución La solución es simple: siempre escapa todo el contenido que pudiera haber sido enviado por un usuario.3.¾qué sucede si accedemos a obtenemos esto: http://example. Este tipo de ataque puede tomar diferentes formas y tiene prácticamente innitas permutaciones.21. Por ejemplo. Esto le permite a un atacante insertar HTML arbitrario en tu página Web. puede encontrarse en aplicaciones Web que fallan a la hora de escapar en forma correcta contenido provisto por el usuario antes de renderizarlo en HTML.1. 'world') return render_to_response("hello. en una oportunidad se encontró que MySpace era vulnerable a un ataque XSS de esta naturaleza. o cambiar sus contraseñas. un atacante no usará algo tan inofensivo como etiquetas <i>. POST.html.backend. 21. esto podría sonar relativamente inofensivo. así que sólo vamos a analizar un ejemplo típico. necesitarás escapar ese nombre en tu código. o cualquiera de los otros escenarios de pesadilla que esta vulnerabilidad hace posibles. Cross-Site Scripting (XSS) El Cross-site scripting (XSS) (Scripting inter-sitio). nombres de tablas o columnas).no el código de MySpace -. podría incluir un fragmento completo de HTML que se apropiara de tu página insertando contenido arbitrario.html".com/hello/?name=Jacob.se ejecutara en tu computadora.GET. CROSS-SITE SCRIPTING (XSS) 221 El método de bajo nivel execute toma un string SQL con marcadores de posición %s y automáticamente escapa e inserta parámetros desde la lista que se le provee como segundo argumento. Esto viola la conanza asumida acerca de que todo el código ubicado en MySpace es realmente escrito por MySpace. Ahora. Los atacantes a menudo usan ataques XSS para robar información de cookies y sesiones. no puedes usar parámetros asociados en todas partes en SQL.get('name'. Cuando construyas SQL en forma manual hazlo siempre de esta manera.3. pero en efecto es un formulario saboteado vía XSS que envía su información bancaria a un atacante. por ejemplo. El problema se complica aun más si almacenas estos datos en la base de datos y luego la visualizas en tu sitio. Un usuario había insertado JavaScript en su página de perl. revisión 789 del 9 de noviembre de 2009 escape (o algo equivalente) cuando visualizas en tu . o para engañar usuarios y lograr que proporcionen información privada a la persona equivocada (también conocido como phishing ). usualmente en la forma de etiquetas <script>. Si simplemente reescribiéramos nuestra plantilla de la siguiente manera: <h1>Hello. En unos pocos días llegó a tener millones de amigos. dicho código agregana lo agregaba a la lista de amigos de todos los usuarios que visitaran su página de perl. no son permitidos como identicadores (esto es.3. Debes usar siempre la etiqueta sitio contenido enviado por el usuario.com/hello/?name=<i>Jacob</i>? En ese caso <h1>Hello. si. MySpace fue muy afortunado de que este código malicioso no hiciera cosas como borrar automáticamente las cuentas de los usuarios que lo ejecutaran. o inundar el sitio con spam. Así que. Este tipo de ataques ha sido usado para engañar a usuarios e inducirlos a introducir datos en lo que parece ser el sitio Web de su banco. {{ name|escape }}!</h1> ya no seríamos vulnerables. Podríamos escribir una plantilla para esta vista de la siguiente manera: <h1>Hello.quote_name.

pero la seguridad es igual de importante. PHP permite que los identicadores de sesión se pasen en la URL (por ejemplo. Tanto el ataque en sí mismo como dichas herramientas son tratados con gran detalle en el Capítulo 14. Session xation (jación de sesión). Un ataque de falsicación de cookies en el cual un atacante sobrescribe los datos almacenados en una cookie que en teoría no son modicables.5. de sesión jo causará que ese usuario comience a usar esa sesión. en la cual un atacante usa un identicador de sesión (posiblemente obtenido mediante un ataque man-in-the-middle) para simular ser otro usuario. nunca será una buena idea conar en nada que se almacene en cookies. SEGURIDAD ¾Porqué simplemente Django no hace esto por mí? Modicar Django para que escape automáticamente todas las variables visualizadas en plantillas es un tópico de frecuente tratamiento en la lista de correo de desarrollo de Django. Un ejemplo de los dos primeros sería una atacante en una cafetería usando la red inalámbrica del lugar para capturar una cookie de sesión. En un nivel aun más sutil. Puede tomar diferentes formas: Un ataque del tipo man-in-the-middle. en el cual in atacante inyecta datos potencialmente peligrosos en la sesión de un usuario -. Es una buena idea revisar la documentación ocial de Django para conocer las novedades respecto a las características de Django. en el cual un atacante espía datos de sesión mientras estos viajan por la red (cableada o inalámbrica).usualmente a través de un formulario que el usuario envía con datos de su sesión. Cross-Site Request Forgery La Cross-site request forgery (CSRF) (Falsicación de peticiones inter-sitio) sucede cuando un sitio Web malicioso engaña a los usuarios y los induce a visitar una URL desde un sitio ante el cual ya se han autenticado -. IsLoggedIn=1 o aun Es trivialmente simple sacar provecho de ese tipo de cookies. Es un asunto no trivial y una decisión de compromiso difícil de evaluar. Este puede luego conectarse al sitio con dicho usuario y obtener datos. Por ejemplo. El Capítulo 12 explica en detalle cómo funcionan las cookies. Session Forging/Hijacking No se trata de un ataque especíco. Ninguna solución automática protegerá tu sitio de ataques XSS el 100 % del tiempo. Todo esto nos lleva. las plantillas Django han evitado este comportamiento debido a que esto cambia sutilmente algo que debería ser un comportamiento no complejo (la visualización de variables). sino una clase general de ataques sobre los datos de sesión de un usuario. nunca sabes quién puede haber estado manoseando las mismas. Session forging (Falsicación de sesión). 21. revisión 789 del 9 de noviembre de 2009 .4. a armar que es muy probable que Django incorpore alguna forma de comportamiento de auto-escaping (o algo cercano a auto-escaping ) en el futuro. Aun si Django incorpora esta característica debes formar el hábito de preguntarte. 21. Agregando comportamiento implícito y oculto va contra los ideales de base de Django (y los de Python). Django incluye herramientas para proteger ante este tipo de ataques. esta será siempre más actual que este libro. especialmente que la versión impresa.com/?PHPSE Un atacante que logre engañar a un usuario para que haga click en un link que posea un identicador Session poisoning (envenenamiento de sesión). entonces. en todo momento. Existe una larga historia de sitios Web que han almacenado una cookie del tipo LoggedInAsUser=jacob. Podría usar esa cookie para hacerse pasar por el usuario original. en la cual un atacante engaña a un usuario y logra asignar un nuevo valor o limpiar el valor existente del identicador de su sesión. y uno de los puntos salientes es que es trivial para los navegadores y usuarios maliciosos el cambiar las cookies sin tu conocimiento. Hasta ahora. ¾De donde provienen estos datos?.222 CAPÍTULO 21. La jación de sesión se ha usado en ataques de phishing para engañar a usuarios e inducirlos a ingresar información personal en una cuenta que está bajo el control de atacante. http://example.por lo tanto saca provecho de su condición de usuario ya autenticado.

Un atacante puede usar esta técnica para enviar spam mediante tu servidor de email. Un atacante podría engañar a un usuario e inducirlo a hacer click en un link que envía un color que en realidad contiene un ataque XSS. si dicho color no está siendo escapado. Cualquier formulario que construya cabeceras de email a partir de datos de un formulario Web es vulnerable a este tipo de ataque. La única cookie que usa el framework de sesiones es un identicador de sesión. Adicionalmente. Si tu sitio permite que usuarios identicados visualicen algún tipo de datos importantes debes. almacena un identicador de sesión que esté relacionado a datos de sesión almacenados en el back-end. La solución Existe un número de principios generales que pueden protegerte de estos ataques: Nunca permitas que exista información sobre sesiones contenida en las URLs. SESSION_COOKIE_SECURE el valor True.6. estaremos permitiéndole construir un conjunto malicioso de cabeceras. por lo tanto.21. y un usuario siempre obtendrá un nuevo identicador de sesión si intenta usar uno no existente. el usuario podría insertar nuevamente código malicioso en el entorno del usuario. esto hará que Django envíe las cookies 21. Eso haría que las cabeceras de email fueran: To: hardcoded@example. Analicemos el formulario de contacto canónico que puede encontrarse en muchos sitios. Nota que ninguno de estos principios y herramientas previene ante ataques man-in-the-middle. A pesar de que es prácticamente imposible detectar a alguien que se ha apropiado de un identicador de sesión. Usualmente el mismo envía un mensaje a una dirección de email ja y. Este campo asunto es usado para construir la cabecera subject del mensaje de email.com Subject: hello cc: spamvictim@example. en cambio.6. Revisa la sección previa sobre XSS y recuerda que esto se aplica a cualquier contenido creado por el usuario así como a cualquier dato enviado por el navegador. la inyección de cabeceras de email. Los identicadores de sesión se almacenan como hashes (en vez de números secuenciales) lo que previene un ataque por fuerza bruta. Si usas el framework de sesiones incluido en Django (o sea request. Recuerda escapar los datos de la sesión si los visualizas en la plantilla. 21. El framework de sesiones de Django (ver Capítulo 12) simplemente no permite que las URLs contengan sesiones. publicar dicho sitio vía HTTPS.com Como en la inyección de SQL. toma control de formularios Web que envían emails. No almacenes datos en cookies en forma directa. siempre. Django incluye protección contra un ataque de sesiones de fuerza bruta. debes asignar a la variable de conguración de sesión vía HTTPS. lo que previene la session xation.session).1.com" (donde "\n" es un caracter de salto de línea). Debes considerar la información de sesiones como datos creados por el usuario. el cuerpo del mensaje y a veces algunos otros campos). Sin embargo. y podrá usar nuestro formulario de contacto para enviar spam. Previene la falsicación de de identicadores de sesión por parte de un atacante siempre que sea posible. INYECCIÓN DE CABECERAS DE EMAIL 223 Un ejemplo canónico es un sitio que almacena un valor de preferencia simple (como el color de fondo de una página) en una cookie.5. a primera vista no parece ser vulnerable a abusos de spam. muchos de esos formularios permiten también que los usuarios escriban su propio asunto para el email (en conjunto con una dirección de. todos los datos de la sesiones se almacenan en la base de datos. Dichos tipos de ataques son prácticamente imposibles de detectar. un atacante podría enviar algo como "hello\ncc:spamvictim@example. si conamos en la línea de asunto enviada por el usuario. revisión 789 del 9 de noviembre de 2009 . Si dicha cabecera no es escapada cuando se construye el mensaje de email. eso es manejado en forma automática. si tienes un sitio con SSL. Inyección de cabeceras de email La hermana menos conocida de la inyección de SQL.

Un ejemplo podría ser una vista que lee archivos desde disco sin limpiar cuidadosamente el nombre de archivo: def dump_file(request): filename = request.GET["filename"] filename = os.1.core.224 CAPÍTULO 21. Directory Traversal Directory traversal se trata de otro ataque del tipo inyección. necesitas limpiar muy cuidadosamente la ruta solicitada para asegurarte que un atacante no pueda escapar del directorio base más allá del cual estás restringiendo el acceso.send_mail BadHeaderError. path = posixpath./..6./etc/passwd.read() # . Otra permutación de este problema yace en código que carga módulos dinámicamente a partir de la URL u otra información de la petición.7. Nota No es necesario decirlo.core.path.. más el asunto). Si intencon un asunto que contenga saltos de línea. una notación corta para el directorio padre).. necesitarás asegurarte de que los saltos de línea en las cabeceras o causan un error o son eliminados. os. De allí en más es sólo una cuestión de tiempo el hecho que descubra el número correcto de puntos para acceder exitosamente. ½incluso un script de reset de base de datos! 21. SafeMIMEText en django. Las funciones de mail incluidas en Django (en tas usar django. django.../..1.view. La solución Si tu código necesita alguna vez leer o escribir archivos a partir de datos ingresados por el usuario. filename) content = open(filename). SEGURIDAD 21. A pesar que parece que la vista restringe el acceso a archivos que se encuentren más allá que si la atacante envía un filename que contenga .static)..mail) simplemente no permiten saltos de línea en ninguno de los campos usados para construir cabeceras (las direcciones de y para.unquote(path)) newpath = '' for part in path.normpath(urllib.mail./. Las vistas que escriben archivos son igual de vulnerables.split('/'): if not part: revisión 789 del 9 de noviembre de 2009 .path. import os import posixpath # . Rails usaba URLs como http://example. El resultado fué que una URL cuidadosamente construida podía cargar automáticamente código arbitrario. BASE_PATH (usando (esto es.mail 21.join(BASE_PATH.com/person/poke/1 directamente para cargar módulos e invocar métodos. pero las consecuencias son doblemente calamitosas. ½nunca debes escribir código que pueda leer cualquier área del disco! Un buen ejemplo de cómo hacer este escaping yace en la vista de publicación de contenido estáticos (en Este es el código relevante: django. Podrías querer examinar la clase para ver cómo implementa esto Django.7. Django arrojará una excepción Si no usas las funciones de email de Django para enviar email.join). Todo aquello que lea archivos sin el escaping adecuado es vulnerable a este problema.. podría acceder a archivos que se encuentren más arriba que BASE_PATH. La solución Podemos prevenir este ataque de la misma manera en la que prevenimos la inyección de SQL: escapando o vericando siempre el contenido enviado por el usuario. dos puntos.. Con anterioridad a mediados del 2006. en el cual un usuario malicioso subvierte código de manejo de sistema de archivos para que lea y/o escriba archivos a los cuales el servidor Web no debería tener acceso./. Un muy público ejemplo se presentó en el mundo de Ruby on Rails. por ejemplo a ..core.

split(part) if part in (os. PythonDebug Off Los usuarios que implementen en conjunto con Apache y mod_python deben también asegurarse que tienen en sus archivos de conguración de Apache. 21. pero la protección que obtendrás para ti y tus usuarios no tiene precio. el visitante debería ver un amistoso mensaje Esta página no está disponible. asegúrate de consultar recursos sobre seguridad más actuales en búsqueda de nuevas vulnerabilidades que pudieran haber sido descubiertas. La losofía de Django es que los visitantes al sitio nunca deben ver mensajes de error relacionados a una aplicación. Sin embargo. así que esta vulnerabilidad no afecta demasiado el código del núcleo. el uso de la abstracción de URLconf signica que Django solo cargará código que le hayas indicado explícitamente que cargue. Adicionalmente.replace('\\'. 21.path. Es cierto que la Web puede ser un mundo salvaje y confuso.8. Dado que los desarrolladores aun necesitan ver los errores que se generan en un sitio en producción.21.1.path. Es más.join(newpath.' and '. Django retornará un mensaje HTTP 500 (Error interno del servidor) y renderizará una plantilla de error provista por ti. En efecto.9. los errores y tracebacks no son para nada útiles para los usuarios nales. Naturalmente. En cambio. os.ni ninguna pista de fragmentos de código o mensajes de error (destinados a programadores) de Python. Si se ja la variable de conguración DEBUG al valor True.. los mensajes de error serán visualizados en el navegador. si esos errores son visualizados una vez que el sitio pasa a producción.pardir): # strip '.serve. '/') Django no lee archivos (a menos que uses la función static. EXPOSICIÓN DE MENSAJES DE ERROR 225 # strip empty path components continue drive. pero con un poco de previsión puedes tener un sitio Web seguro.html y debe estar situada en la raíz de uno de tus directorios de plantillas. Exposición de mensajes de error Mientras se desarrolla.splitdrive(part) head. pueden revelar aspectos de tu código o conguración que podrían ser de utilidad a un atacante.' in path continue newpath = os. De otra forma. Django posee mensajes de depuración vistosos e informativos especícamente para hacer la tarea de depuración más fácil. Palabras nales sobre la seguridad Esperamos que toda esta exposición sobre problemas de seguridad no sea demasiado intimidante. Si tu código genera una excepción no tratada. tener la posibilidad de ver tracebacks y errores en vivo en tu navegador es extremadamente útil. todos los errores que se manejen de esta manera dispararán el envío de un email con el traceback completo a las direcciones de correo conguradas en la variable ADMINS. No existe manera de crear una URL que cause que Django cargue algo no mencionado en una URLconf. si estás leyendo la versión en papel de este libro. esto suprimirá cualquier error que pudiera ocurrir aun antes de que Django se haya cargado.8. 21. un visitante al sitio no debería ver un traceback completo -.curdir. Esta plantilla de error tiene el nombre 500. por supuesto. Es una pequeña inversión a realizar. Así que el framework debería ocultar todos los mensajes de error al público pero debería mostrarlos a los desarrolladores del sitio.8. Ten en mente que la seguridad Web es un campo en constante cambio. pero en ese caso está protegida por el código recién mostrado). revisión 789 del 9 de noviembre de 2009 . part). los desarrolladores necesitan ver tracebacks para depurar problemas en su código. siempre es una buena idea dedicar algún tiempo semanalmente o mensualmente a investigar y mantenerse actualizado acerca del estado de la seguridad de aplicaciones Web. part = os. La solución Django tiene un sencillo control que gobierna la visualización de esos mensajes de error.path. part = os.

SEGURIDAD 21. En el ¾Qué sigue? `próximo capítulo`_. nalmente trataremos las sutilezas de la implementación de Django: como lanzar un sitio de producción y como dotarlo de escalabilidad.10.226 CAPÍTULO 21. Duplicate explicit target name: próximo capítulo. revisión 789 del 9 de noviembre de 2009 .

¾Qué es LAMP? El acrónimo LAMP fue originalmente acuñado para describir un conjunto de software open source utilizado para propulsar muchos sitios web: Linux (sistema operativo) Apache (servidor web) MySQL (base de datos) PHP (lenguaje de programación) A lo largo del tiempo. y Django). De hecho. Apache. Django también debe ser capaz de escalar hacia arriba para conocer las necesidades de grandes empresas y corporaciones. Las motivaciones para este objetivo se vuelven evidentes cuando observas el trasfondo de Django: un pequeño periódico en Kansas difícilmente pueda costear hardware de servidor de última tecnología. Apache. este enfoque en el desempeño y la facilidad de implementación se tornó importante por diferentes razones: los desarrolladores acionados tienen los mismos requerimientos. Los individuos que quieren usar Django están encantados de saber que pueden hospedar un sitio con tráco entre pequeño y mediano por menos de u$s 10 mensuales. por años los desarrolladores de Django actuaron como sus propios administradores de sistema -. Como Django se volvió un proyecto open source. la losofía shared nothing se trata de el acoplamiento débil aplicado a todo el conjunto de software utilizado.ya que simplemente no había suciente harware como para necesitar administradores dedicados a esa tarea -. hemos mencionado algunos objetivos que conducen el desarrollo de Django. Facilidad de uso. las losofías probadas por los agrupamientos tipo LAMP permanece en la mentalidad de implementación de Django.todos esas metas marcaron el camino de los desarrolladores. Usa Django y consigue un PAID! (N. Nada Compartido Esencialmente.Capítulo 22 Puesta en marcha de Django en un servidor A lo largo de este libro.: En inglés. por lo que los desarrolladores originales de Django trataron de extraer el máximo desempeño posible de los escasos recursos disponibles. Sin embargo. PAID signica pago). y debería poder servir una gran cantidad de tráco con recursos limitados. Por ello. PostgreSQL. de T. abstracción de tareas repetitivas -. 22. Aquí. Han habido algunos (más que nada cómicos) intentos de acuñar acrónimos similares para describir los agrupamientos de tecnología que usa Django. amigabilidad para nuevos programadores. desde la concepción de Django. Django adopta una losofía común entre los grupos de software del tipo LAMP que suele llamarse shared nothing (nada compartido). Pero ser capaz de escalar hacia abajo es solamente la mitad de la batalla. aunque Django usa Python y es agnóstico respecto al motor de base de datos a utilizar. Esta arquitectura se presentó como respuesta directa a la que en su momento prevalecía: una aplicación de revisión 789 del 9 de noviembre de 2009 . Los autores de este libro están encariñados con LAPD (Linux.incluso manejando sitios con decenas de millones de entradas por día. Internet.1. el acrónimo se ha vuelto más una referencia a la losofía de este tipo de agrupamiento de software que a cualquiera de estos en particular. ha existido siempre otro objetivo importante: Django debería ser fácil de implementar. y Django) o PAID (PostgreSQL.

un rápido comentario. Como probablemente esperabas... las aplicaciones Web deben dejar de asumir que el mismo servidor es el que maneja cada petición -. si un servidor web falla. En una implementación LAMP (y Django) de gran escala.: ½Tendremos lo que ellos tienen! 22. revisión 789 del 9 de noviembre de 2009 . cualquier dato que deba estar disponible entre múltiples solicitudes. No obstante. se agregaría un servidor NFS.o incluso las distintas partes de una petición. cuestan decenas o a veces centenas de miles de dolares. se podría fácilmente comenzar con un servidor barato y simplemente ir agregando más servidores baratos a medida que se crece. ½más de media docena de servidores pueden estar involucrados en servir una sola petición! Las repercusiones a esta situación son numerosas. En otras palabras. Recuerda. esto puede ser un problema serio. Estos servidores. pero ¾funciona realmente? Bueno. en vez de responder directamente.e incluso partes del sistema operativo -.ninguna parte de Django viola estos principios -. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR servidor web monolítica que encapsulaba el lenguaje. O a nivel hardware. Probablemente reconozca algunos de estos nombres: Amazon Blogger Craigslist Facebook Google LiveJournal Slashdot Wikipedia Yahoo YouTube Parafraseando la famosa escena de Cuando Harry conoció a Sally. Cuando llega el momento de escalar. Si Apache por alguna razón no funciona para la implementación dada. Aunque para que esto funcione. por lo que debe ser capaz de conectarse a servidor de base de datos remoto. El software no puede asumir que los recursos son locales. por supuesto. debe almacenarse en algún tipo de almacenamiento permanente como la base de datos o una caché centralizada. Java).un único proceso (por ejemplo. Django maneja todo esto más o menos de forma transparente -. dejando a los sitios web de gran escala lejos del alcance de individuos o pequeñas compañías con buenas ideas pero sin efectivo. Fallas en maquinas individuales deben estar contempladas. ¾Pero esto funciona? Esta losofía puede sonar bien en papel (o en tu pantalla). por lo que las aplicaciones monolíticas requieren servidores enormemente potentes. deberías ser posible cambiarlo por otro servidor con mínimas complicaciones. Si un servidor de base de datos de u$s3000 ya no puede manejar la carga.pero conocer la losofía ayuda cuando es tiempo de escalar. pero pueden reducirse a estos puntos: El estado no puede ser guardado localmente. o un cuarto) hasta que pueda.2.228 CAPÍTULO 22. permítanos mostrarle una lista no muy exhaustiva de compañías que han basado sus negocios en esta arquitectura. esta losofía sobre implementación se basa enteramente en hardware barato. Por ejemplo. Un nota sobre preferencias personales Antes de entrar en detalles. la plataforma web no puede asumir que la base de datos corre en el mismo servidor. Si se necesita más capacidad de almacenamiento. es casi imposible separar el trabajo de un proceso monolítico entre muchas maquinas físicas diferentes. lo que la comunidad LAMP descubrió fue que si se separa cada pieza de esa pila de software Web en componentes individuales. debería ser posible reemplazarlo por otra maquina con ínmos tiempos de caída. sencillamente se compraría un segundo (o un tercero. la base de datos y el servidor web -. Cada pieza del conjunto debe ser fácilmente trasladable o reemplazable.

Pero para permitir un debate completo lo explicitaremos aquí. Nosotros tratamos de permanecer lejos de esas batallas.djangoproject. Third Edition (O'Reilly. Usando Django con Apache y mod_python Apache con mod_python es actualmente la conguración más robusta para usar Django en un servidor en producmod_python (http://www. Pro Apache.3. la mayoría de las veces hemos tratado de evitarlo. por sobre el MPM worker.x y mod_python 3. edite su archivo de conguración de Apache y agregue lo siguiente: <Location "/"> SetHandler python-program PythonHandler django.modpython SetEnv DJANGO_SETTINGS_MODULE misitio. ción.3. Preferimos lo siguiente: Linux (especícamente Ubuntu) como nuestro sistema operativo Apache y mod_python para el servidor web PostgreSQL como servidor de base de datos Por supuesto. mucha tinta (digital) ha sido despilfarrada argumen- vi). Puede parecerse a esto: LoadModule en tu archivo de conguración de LoadModule python_module /usr/lib/apache2/modules/mod_python.por supuesto -. 2004) de Peter Wainwright. Simplemente no hay tiempo suciente.22. Third Edition (Apress. y constantemente nos preguntar por nuestras preferencias. Django requiere Apache 2. primero debe asegurarse de que tiene Apache instalado con el módulo mod_python activado. Algunos de los que nos gustan son los siguientes: La documentación gratuita de Apache. 22. disponible via djangoproject. Esto usualmente signica tener una directiva Apache. y nosotros preferimos el módulo de multiprocesamiento (MPM) prefork de Apache.x. y -. http://www.lenguajes de programación.com/r/books/pro-apache/ rie y Peter Laurie. Sin embargo. Nota Congurar Apache está claramente más allá del alcance de este libro. El código permanece en memoria a lo largo de la vida del proceso Apache. disponible via http://www.djangoproject.handlers.com/r/apache/docs/ http://www. motores de base de datos (MySQL versus PostgreSQL).settings PythonDebug On </Location> revisión 789 del 9 de noviembre de 2009 .djangoproject.core.com/r/mod_python/) es un plugin de Apache que embebe Python dentro de Apache y carga código Python en memoria cuando el servidor se inicia.so Luego.3. disponible via Apache: The Denitive Guide.sistemas operativos (Linux versus Windows versus Mac OS).1. USANDO DJANGO CON APACHE Y MOD_PYTHON 229 tando sobre editores de textos (emacs versus El open source es famoso por sus llamadas guerras religiosas. Afortunadamente existen grandes recursos disponibles para aprender más sobre Apache. por lo que simplemente mencionaremos algunos detalles que necesitamos. 2002) de Ben Lau- apache-pra/ 22. Conscientes de que explicitar esas preferencias puede encender una de de esas batallas ya mencionadas. lo que repercute en aumentos signicativos de desempeño comparado con otros arreglos de servidor. podemos indicarles muchos usuarios de Django que han hecho otras elecciones con gran éxito.com/r/books/ Conguración básica Para congurar Django con mod_python. hay algunas elecciones que tomar al momento de implementar Django.

y cualquier petición a tu sitio (o a tu host virtual si pusiste las directivas dentro de un bloque será servida por Django. en algun lugar más profundo que / -Django no recortará el prejo de la URL para tu URLpatterns. Para lograr esto.urls')).com # .modpython SetEnv DJANGO_SETTINGS_MODULE misitio. simplemente puede hacer descender el nivel de tu URL usando una cuña de URLconf: urlpatterns = patterns(''. de Django.handlers. Nota que estamos usando la directiva Apache comunmente corre como un usuario diferente de tu usuario normal y puede tener una ruta y un sys... <VirtualHost>) Reinicia Apache. simplemente usa VirtualHost así: NameVirtualHost * <VirtualHost *> ServerName www.3.esto es. Nota Si implementas Django en un subdirectorio -.settings PythonDebug On </Location> entonces todos tus patrones de URL deberán comenzar con "/misitio/".other_settings </VirtualHost> revisión 789 del 9 de noviembre de 2009 . Esta última se utiliza para apuntar a lugares de nuestra sistema de archivos.path Esto le dice a Apache. Ten en cuenta que deberias congurar PythonDebug Off en un servidor de producción. usando el manejado mod_python distintos.2. SetEnv DJANGO_SETTINGS_MODULE misitio. Entonces. Puedes necesitar decirle a mod_python cómo encontrar tu proyecto y a Django mismo: PythonPath "['/ruta/al/proyecto'.path" También puedes agregar directivas como PythonAutoReload O ` para ajustar la performance.core. Alternativamente. SetEnv DJANGO_SETTINGS_MODULE misitio. Mira la documentación de mod_python para un obtener un listado completo de opciones. ) 22. Por esta razón es que usualmente recomendamos implementar Django sobre la raiz de tu dominio o host virtual. <Location> y no <Directory>.com # .. tus usuarios verán feas trazas de error de Python si algo sale dentro de mod_python. si tu conguración de Apache luce como esto: <Location "/misitio/"> SetHandler python-program PythonHandler django. Usa mod_python para cualquier URL en '/' o bajo ella. <Directory> no tendría sentido aquí.ejemplo. Le pasa el valor de DJANGO_SETTINGS_MODULE de modo que mod_python conoce que conguración utilizar. include('normal.settings por el DJANGO_SETTINGS_MODULE apropiado para tu sitio. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR Asegurese de reemplazar misitio. mientras que <Location> apunta a lugares en la estructura de la URL de un sitio web.. Si dejas PythonDebug On.230 CAPÍTULO 22.ejemplo. Corriendo multiples instalaciones de Django en la misma instancia Apache Es enteramente posible correr multiples instalaciones de Django en la misma instancia de Apache.root. (r'^misitio/'. Probablemente quieras hacer esto si eres un desarrollador web independiente con multiples clientes pero un sólo un único servidor.settings </VirtualHost> <VirtualHost *> ServerName www2. '/ruta/a/django'] + sys.

probablemente quieras usar el paquete de registro de eventos estándar de Python (Python's standard logging package).22.png: media y cualquier URL que termine en <Location "/"> SetHandler python-program PythonHandler django. si no tienes opción para servir los archivos multimedia que no sea el mismo usa Django. Usa la directiva <Location> PythonInterpreter a interpretes distintos: <VirtualHost *> ServerName www. uno que no está corriendo a la vez Django) para servir estos archivos.modpython SetEnv DJANGO_SETTINGS_MODULE mysite. Por ejemplo.org/lib/module-logging. <Location "/algo"> SetEnv DJANGO_SETTINGS_MODULE misitio.settings </Location> <Location "/media/"> SetHandler None </Location> revisión 789 del 9 de noviembre de 2009 . USANDO DJANGO CON APACHE Y MOD_PYTHON 231 Si necesitar poner dos instalaciones de Django sobre el mismo para brindar diferentes directivas VirtualHost. Para más información.jpg. por lo que aqui compartimos un pequeño truco para evitarlo: simplemente agrega tus privilegios Django.handlers. video.. Corriendo un servidor de desarrollo con mod_python Debido a que mod_python cachea el código python cargado. cuenta que MaxRequestsPerChild 1 a tu archivo de conguración para forzar a Apache a recargar todo con cada petición. Sin embargo.3.3. Alternativamente. ten en print no tiene efectos sobre mod_python.core.3.html. mira la sección  Escalamiento.settings PythonInterpreter misitio </Location> <Location "/otracosa"> SetEnv DJANGO_SETTINGS_MODULE misitio. Pero no hagas esto en un servidor de producción. esto congura Django en la raiz del sitio pero deshabilitando Django para el subdirectorio . Si necesitas imprimir información de depuración en una conguración mod_python. necesitar prestar especial atención para asegurarte de que el caché de código de mod_python no mezcle las cosas.python.gif. http://docs. estas no aparecen en el log de Apache como pudrías esperar. Sirviendo Django y archivos multimedia desde la misma instancia Apache Django no debería ser utilizado para servir archivos multimedia (imágen. Tambien puedes usar <LocationMatch> para comparar con una expresión regular.com # .other_settings PythonInterpreter misitio_otro </Location> </VirtualHost> Los valores de entes.3.ejemplo. . PythonInterpreter no importante realmente ya que se encuentran en dos bloques Location difer- 22. Hay más información disponible en ración a las plantillas de tu página. . cuando implemantas sitios Django sobre mod_python necesitarás reiniciar Apache cada vez que realizar cambios en tu código. ash) por sí mismo. Esto puede ser tedioso. audio. Recomendamos usar un servidor Web separado (es decir..4. puedes agregar la información de depu- 22. aquí te mostramos como desactivar mod_python para una parte particular del sitio: VirtualHost Apache que <Location "/media/"> SetHandler None </Location> Cambia Location o a la URL raiz donde se encuentran tus archivos. mejor deja ese trabajo al servidor web que hayas elegido. o revocaremos Si eres el tipo de programador que depuran dispersando sentencias print por el código (nosotros somos).

de manera de encontrar el módulo especíco que es el culpable.com/r/articles/getting-modpython-working/. y ListDLLs (de SysInternals) en Windows pueden ayudarte a indenticar dependencias compartidas y posibles conictos de version. Cuando esto sucede. estos no se propagan al nivel de Apache y no aparecerán en el error_log del servidor. logrando que las peticiones sean servidas sin tiempo de inicialización. Este volcado de error se difunde por multiples líneas. Manejo de errores Cuando usas Apache/mod_python. tus modelos. lo que puede entrar en conicto con la versión embebida en Apache. FastCGI además puede ser más liviano que Apache.djangoproject. Descripción de FastCGI FastCGI es una manera eciente de dejar que una aplicación externa genere páginas para un servidor Web.4. FastCGI permite mayor seguridad y posiblemente una mejor performance que mod_python.4. En este caso. quien ejecuta el código y devuelve la respuesta al servidor. y accede a la URL correspondiente desde tu navegador. verás una página Internal Server Error en tu navegador.en otras palabras. Profundiza en los módulos y revisa sus importaciones si es necesario. la remitirá al navegador del cliente. La excepción a esto sucede si algo está realmente desordenado en tu conguración Django. A diferencia de mod_python. Si esto causa un colapso. es feo y bastante difícil de leer. revisión 789 del 9 de noviembre de 2009 . (Sí. Hay información detallada en un listado FAQ de mod_python. En algunos casos. 22.5. una buena cosa para hacer es poner un esqueleto de sitio sobre mod_python a funcionar. accesible via com/r/articles/php-modpython-faq/ http://www. y así. Para sitios pequeños. sino en un proceso separado y persistente. Usando Django con FastCGI Aunque Django bajo Apache y mod_python es la conguración más robusta de implementación. El siguiente paso debería ser editar tu código de pruebas y agregar la importación de cualquier código especíco de Django que estes usando -. Puede deberse a que estás corriendo mod_python y mod_php sobre la misma instancia de Apache.com/r/articles/expat-apache-crash/. con MySQL como motor de base de datos. a su turno. El servidor delega las peticiones Web entrantes (a través de un socket) a FastCGI. los errores serán canalizados por Django -. Incluye estas importaciones en tu función de gestión de pruebas. 22.6. http://www. en inglés) cuando instalas Django. otool en Mac OS. DocumentRoot para que Apache sepa dónde encontrar tus 22. quien. en algunas situaciones.tus vistas. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR <LocationMatch "\. sin el framework Django.djangoproject. Para información detallada. Esta es una manera fácil de aislar los problemas especícos de mod_python. FastCGI permite que el código permanezca en memoria. un proceso FastCGI no corre dentro del proceso del servidor Web. mucha gente usa hosting compartido.(jpg|gif|png)$"> SetHandler None </LocationMatch> En todos estos casos. tu URLconf. necesitarás congurar la directiva archivos estáticos. Como mod_python. Si continuas teniendo problemas para congurar mod_python. en los que FastCGI es la única opción de implementación. esto ocasiona un conocido problema que mod_python tiene debido a conictos de versión en PHP y el back-end MySQL de la base. y el volcado de error (traceback) de Python completo en tu archivo error_log de Apache. la conguración de RSS. se trata casi siempre de una o dos causas no muy relacionadas con Django en sí: Puede ser que tu código Python está importando el módulo Causing Apache Crash en pyexpat (usado para parseo XML). habrás conrmado que es la importación de código Django la causa del problema. revisa Expat http://www.1. El artículo Getting mod_python Working detalla el procedimiento: djangoproject. Manejando fallas de segmentación Algunas veces.3. pero así como mod_python hace las cosas).3. Apache produce fallas de segmentación (Segmentation faults. herramientas de sistema como ldconfig en Linux.232 CAPÍTULO 22. Gradulamente reduce el conjunto de importaciones hasta que el colapso desaparezca. Adicionalmente. 22. Para más ayuda.

/manage./manage. por lo cual http://www.py). Un servidor Web puede conectarse a un servidor FastCGI de dos formas: usando un socket de dominio Unix. primero cambia al directorio de tu proyecto (donde está tu con el comando runfcgi: manage. Entonces.py runfcgi [options] Si especicas Necesitarás especicar un help como única opción después de runfcgi.sock pidfile=django. Algunos ejemplos pueden ayudar a explicarlo: Ejecutar un servidor 'threaded' en un puerto TCP: . Cada proceso Apache consigue una copia completa del motor de Apache. y en la mayoría de los casos estarás iniciando el proceso servidor FastCGI por tu cuenta./manage. (un named pipe en sistemas Win32) o un socket TCP.1 port=3033 Ejecutar un servidor preforked sobre un socket de dominio Unix: . lo hace al costo de ocupar espacio en memoria. USANDO DJANGO CON FASTCGI 233 ¾Por qué ejecutar código en un proceso separado? Los módulos tradicionales mod_* en Apache embeben varios lenguajes de scripting (los más no- tables son PHP.4. es fácil detenerlo: simplemente presiona Ctrl+C para detenerlo y salir del servidor FastCGI. una biblioteca Python para 22. cuando congures tu servidor Web. Para iniciar tu servidor. probablemente estés forzado a usar procesos FastCGI manejados por el Web server. Nota Si estás en un sistema de hosting compartido. solo tienen el overhead de memoria de Python y Django.pid Ejecutar sin demonizar (ejecutar en segundo plano) el proceso (es bueno para el debugging): . más abajo.py . Debido a la naturaleza de FastCGI. por otro lado. y Perl/mod_perl) dentro del espacion de procesos de tu servidor Web. Los procesos FastCGI. debes leer la sección titulada  Ejecutando Django en un proveedor de Hosting compartido con Apache. o algún otro) hace contacto con tu proceso DjangoFastCGI solo cuando el servidor necesita cargar una página dinámica.com/r/flup/.2.4.0. y ejecuta manage.py runfcgi method=threaded host=127. necesitas instalar puedes querer utilizar la última versión SVN. puede servir la respuesta muy rápido. Algunos usuarios han reportado páginas que explotaron con versiones antiguas de flup en flup.py runfcgi.djangoproject. Como el demonio ya está ejecutando su código en memoria. Antes de que puedas empezar a usar FastCGI con Django. Si especicas la opción forma: kill de pidfile en manage. puedes detener el demonio FastCGI en ejecución de esta kill `cat $PIDFILE` revisión 789 del 9 de noviembre de 2009 . socket o si no host y port. Puedes conseguir manejar FastCGI.py runfcgi method=prefork socket=/home/user/mysite. flup.sock Detener el Demonio FastCGI Si tienes el proceso ejecutando en primer plano.py runfcgi daemonize=false socket=/tmp/mysite. Tu servidor Web (ya sea Apache. Si estás en esta situación. también es posible tener procesos ejecutando bajo una cuenta de usuario diferente de la del proceso del servidor Web. Lo que elijas es una cuestión de preferencias. necesitarás recurrir al comando Unix. lighttpd. se mostrará una lista de todas las opciones disponibles. Python/mod_python. Este es un buen benecio de seguridad es sistemas compartidos. usualmente un socket TCP es más fácil debido a cuestiones de permisos. con todas las características de Apache que Django simplemente no aprovecha.22. Si estás tratando con procesos en segundo plano. A pesar de que esto reduce el tiempo de inicio (porque el código no tiene que ser leído del disco para cara consulta).0. Ejecutando tu Servidor FastCGI FastCGI opera sobre un modelo cliente/servidor./manage. solo necesitas apuntarlo al socket o host/port que especicaste cuando iniciaste el servidor FastCGI. dado que signica que puedes asegurar tu código de otros usuarios.

34.fcgi (o donde hayas especicado en la directiva como se explicó en la sección anterior). Probablemente éste sea el caso más común. En este ejemplo.) Usando mod_rewrite para apuntar URLs hacia FastCGI El segundo paso es decirle a Apache que use FastCGI para las URLS que coincidan con cierto patrón. esto.djangoproject.0.com/r/mod_fastcgi/FastCGIExternalServer/). Necesitarás hacer dos cosas: tu servidor FastCGI.56.com DocumentRoot /home/user/public_html Alias /media /home/user/python/django/contrib/admin/media RewriteEngine On RewriteRule ^/(media.pid" SOCKET="$PROJDIR/mysite. Para reiniciar con facilidad tu demonio FastCGI en Unix.234 CAPÍTULO 22.djangoproject. con mod_fastcgi inUna vez que hayas completado la conguración. Especicando la Localización del Servidor FastCGI La directiva FastCGIExternalServer socket o un le dice a Apache como encontrar tu servidor FastCGI. usa el módulo mod_rewrite y reescribe las URLs hacia mysite. le decimos a Apache que use FastCGI para manejar cualquier consulta que no represente un archivo del sistema de archivos y no empiece con el sitio de administración de Django: /media/. then kill `cat -. necesitarás que Apache esté instalado y congurado.py runfcgi socket=$SOCKET pidfile=$PIDFILE 22.0.sock" cd $PROJDIR if [ -f $PIDFILE ].fcgi -host 127. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR donde $PIDFILE es el pidfile que especicaste.3.conf Usar (de la conguración de Apache).sock # Connect to FastCGI via a TCP host/port: FastCGIExternalServer /home/user/public_html/mysite. PROJDIR="/home/user/myproject" PIDFILE="$PROJDIR/mysite.*)$ /$1 [QSA. Aquí hay ejemplos de ambos: # Connect to FastCGI via a socket/named pipe: FastCGIExternalServer /home/user/public_html/mysite. Es solo una URL usada por el servidor Web internamente -. apunta Apache a tu instancia FastCGI de Django editando el archivo stalado y habilitado. Usar la directiva FastCGIExternalServer para especicar la localización de mod_rewrite para apuntar las URLs a FastCGI según sea necesario.L] revisión 789 del 9 de noviembre de 2009 . PAra hacer FastCGIExternalServer. aunque el archivo /home/user/public_html/mysite.1:3033 En los dos casos. el directorio /home/user/public_html/ debe existir. Usando Django con Apache y FastCGI Para usar Django con Apache y FastCGI.c httpd.\ PYTHONPATH=". pedes usar este breve script en la línea de comandos: #!/bin/bash # Replace these three settings. puedes especicar un host..un enganche para indicar que las consultas en esa URL deben ser manejadas por FastCGI. Consulta la documentación de Apache y mod_fastcgi para instrucciones detalladas: http://www. si estás usando <VirtualHost 12.fcgi no necesariamente tiene que existir./python:. Como se explica en los documentos de FastCGIExternalServer (http://www./manage.$PIDFILE` rm -f -. (Más sobre esto en la siguiente sección.78> ServerName example.fcgi -socket /home/user/mysite.$PIDFILE fi exec /usr/bin/env ." \ .4..

com/r/lighttpd/) es un servidor Web liviano usado habitualmente para servir archivos estáticos.ico$" => "/media/favicon.server = ( "/mysite..server = ( .*)$" => "$1". en algún lugar después de mod_rewrite y mod_access.document-root = "/home/user/public_html" fastcgi. $HTTP["host"] == "www.22. y Agrega lo siguiente a tu archivo de conguración de lighttpd: server. solo agrega un bloque condicional en torno a tu conguración FastCGI para cada sitio: # If the hostname is 'www..example1.1". "check-local" => "disable".example2.0.4. "^(/.. } Puedes también ejecutar múltiples instalaciones de Django en el mismo sitio simplemente especicando múltiples entradas en la directiva fastcgi. revisión 789 del 9 de noviembre de 2009 .L] </VirtualHost> 22. # "port" => 3033. Asegúrate que antes de mod_fastcgi está en tu lista de modulos. Para especicar múltiples sitios FastCGI. } # If the hostname is 'www.*)$ /mysite.. USANDO DJANGO CON FASTCGI 235 RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^/(.ico".fcgi/$1 [QSA.. ) url. FastCGI y lighttpd lighttpd (http://www. "^/favicon\. para servir medios de administración.com'..document-root = "/foo/site2" fastcgi. ) .0.. ) .djangoproject. "socket" => "/home/user/mysite. Agrega un host FastCGI para cada una. ) alias. Admite FastCGI en forma nativa y por lo tanto es también una opción ideal para servir tanto páginas estáticas como dinámicas. Probablemente desees también mod_alias.com" { server..url = ( "/media/" => "/home/user/django/contrib/admin/media/".fcgi$1".example1..server = ( .server. ) ).. ) Ejecutando Múltiples Sitios Django en Una Instancia lighttpd lighttpd te permite usar conguración condicional para permitir la conguración personalizada para cada host.. $HTTP["host"] == "www..rewrite-once = ( "^(/media.com" { server.example2.com'. mod_accesslog.fcgi" => ( "main" => ( # Use host / port instead of socket for TCP fastcgi # "host" => "127.4. si tu sitio no tiene necesidades especícas de Apache.4.document-root = "/foo/site1" fastcgi.sock".*)$" => "/mysite.

y hablaremos exclusivamente acerca de escalamiento bajo Apache y mod_python.core. Sólo volver a subir Django por ti. Crea un archivo colócalo en tu directorio Web. haremos una buena presuposición.L] mysite.conf. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR 22.de manera que la fecha y hora del archivo cambien. Es importante notar.4. Primero. En el directorio raíz de tu Web. y asegúrate de hacerlo ejecutable: #!/usr/bin/python import sys. La siguiente cobertura debe ser suciente para mostrat el principio general. veamos como puedes escalar una instalación Django. Después. Pero no hay necesidad de reiniciar Apache en este caso the. revisión 789 del 9 de noviembre de 2009 . y cuando sea posible.chdir("/home/user/myproject") # Set the DJANGO_SETTINGS_MODULE environment variable. archivo Ejecutando Django en un Proveedor de Hosting Compartido con Apache En estos casos. Esta sección explica como puede escalar un sitio desde un servidor único a un cluster de gran escala que pueda servir millones de hits por hora.5. crea un pequeño script que le diga a Apache como iniciar tu programa FastCGI.5.environ['DJANGO_SETTINGS_MODULE'] = "myproject.fcgi RewriteEngine On RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^(. escalando según lo necesite. no necesitas iniciar el servidor FastCGI por tu cuenta.*)$ mysite. reiniciará tu aplicación Si tienen acceso a la línea de comandos en un sistema Unix system.path.servers. Escalamiento Ahora que sabes como tener a Django ejecutando en un servidor simple. necesitarás decirle a FastCGI que el código ha cambiado. (Optional.fcgi. agrega esto a un archivo llamado . os. aún es posible ejecutar Django usando procesos iniciados por el sevidor Web. como se explica en esta sección. por lo que escalar es cualquier cosa menos una operación de una solución única para todos los casos. estamos mucho más familiarizados con Apache.) # os. trateremos de señalar donde se puedan elegir distintas opciones.236 CAPÍTULO 22. A pesar de que conocemos vario casos exitosos de desarrollos FastCGI medios y grandes. sin embargo. os # Add a custom Python path.settings" from django. que cada sitio grande es grande de diferentes formas. Nota Si estás usando procesos iniciados por el servidor Web.o editar el archivo -.fastcgi import runfastcgi runfastcgi(method="threaded".fcgi -. "/home/user/python") # Switch to the directory of your project.insert(0. Apache iniciará una cantidad de procesos. sys. Muchos proveedores de hosting compartido no te permiten ejecutar tus propios demonios servidores o editar el httpd. Cuando Apache ve que el archivo ha sido actualizado.htaccess AddHandler fastcgi-script .fcgi/$1 [QSA. puedes hacer esto fácilmente usando el comando touch: touch mysite. daemonize="false") Reiniciando el Server Iniciado Si cambias cualquier código Python en tu sitio.fcgi 22. mysite.

Aqui es donde empezamos a movernos hacia lo que usualmente se llama arquitectura n-tier. y es relativamente barato -. probablemente sea una buena idea empezar a pensar en pooling de conexiones y/o replicación de bases de datos. si anticipas que en algún momento vas a necesitar crecer más allá de un servidor de base de datos único.1: conguración de Django en un servidor único . CPU) que preeren monopolizar. Así que el siguiente paso es separar los medios -. Estas dos actividades tienen su mejor performance bajo distintas circunstancias.2. no hay suciente espacio para hacerle justicia a estos temas en este libro. y encerrándolas en la misma caja terminarás con que ninguna de las dos tendrá particularmente buena performance.5. Sin embargo.puedes instalar un servidor único diseñado para Django por menos de 3. y cuando corren en el mismo servidos siempre terminan peleando por los mismos recursos (RAM. ya que depender de la DNS para la conexión entre el servidor Web y el 22.a un servidor dedicado (ver Figura 20-3). nuestra arquitectura ahora se ve como en la Figura 20-2. Probablemente sea una buena idea usar la IP si es posible. así que vas a necesitar consultar la documentación y/o a la comunidad de tu base de datos para más información. No te asustes por la terminilogía -.3.sólo se reere al hecho de que diferentes tiers de la pila Web separadas en diferentes máquinas físicas. Desafortunadamente. todo lo que no es generado por una vista de Django -. Figura 22. DATABASE_HOST a la IP o nombre DNS de tu servidor. Con un servidor de base de datos separado. como se explica en la siguiente sección. Ejecutando un Servidor de Medios Separado Aún tenemos un gran problema por delante desde la conguración del servidor único: el servicio de medios desde la misma caja que maneja el contenido dinámico. A esta altura. Separando el Servidor de Bases de Datos En lo que tiene que ver con Django.22.5. Esto se resuelve fácilmente moviendo el servidor de base de datos a una segunda máquina.1.5. el proceso de separar el servidor de bases de datos es extremadamente sencillo: simplemente necesitas cambiar la conguración de servidor de bases de datos no se recomienda. ESCALAMIENTO 237 22.5.esto es. 22. con una arquitectura que se ve como en la Figura 20-1. revisión 789 del 9 de noviembre de 2009 . Los servidores de base de datos y los servidores Web adoran tener el servidor entero para ellos. Esto funciona bien para sitios pequeños y medianos.000 dólares. caerás rápidamente en contención de recursos entre las diferentes piezas de software. a medida que el tráco se incemente. Ejecutando en un Servidor Único LA mayoría de los sitios empiezan ejecutando en un servidor único.

2: Moviendo la base de datos a un servidor dedicado. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR Figura 22.238 CAPÍTULO 22. Figura 22.3: Separando el servidor de medios. revisión 789 del 9 de noviembre de 2009 .

puedes agregar más máquinas balanceadoras de carga y distribuir entre ellas usando DNS round-robin. revisión 789 del 9 de noviembre de 2009 . ya hemos separado las cosas todo lo posible. y los procesos FastCGI back-end reemplaza a los servidores Apache/mod_python/Django. el uploading 22. necesitarás agregar servidores de base de datos relicados. El administrador de Django necesita poder escribir medios 'subidos' en el servidor de medios.simplemente copia el código en varias máquinas. La manera más fácil de hacer esto es usar el NFS para montar los directorios de medios del servidor de medios en el servidor Web (o los servidores Web). de todas formas necesitas organizar una forma de que esa escritura se pueda hacer a MEDIA_ROOT. pueder agregar y eliminar servidores Web back-end sin perder un segundo fuera de servicio. lighttpd y tux (http://www. Es fácil tener múltiples copias de un sitio Djando ejecutando en diferente hardware. Yendo a lo grande En este punto. Esta conguración de tres servers debería manejar una cantidad muy grande de tráco -.com/r/tux/) son dos excelentes elecciones aquí. pero un servidor Apache bien 'pelado' también puede funcionar. Nota Si estás usando FastCGI.4. puedes agregar más servidores al cluster.5. En realidad. Si necesitas más almacenamiento cache. respectivamente.5. En cualquier etapa. puedes agregar servidores de cache dedicados. Si los montas en la misma ubicación apuntada por de medios Simplemente Funciona¾. Para este ejemplo. Para sitios pesados en contenidos estáticos (fotos.djangoproject. El servidor front-end se convierte esencialmente en el balanceador de carga. pero existen algunos balanceadores de carga por software de alta calidad que son open source. esto es algo bueno. Una vez que tienes un balanceador de carga en el frente. sino también conabilidad. los usuarios de PostgreSQL deberían mirar a Slony (http://www. mod_proxy de Apache es una opción.nosotros servimos alrededor de 10 millones de hits por día con una arquitectura de este tipo-. optimizado para la entrega de medios MEDIA_ROOT controla donde se esciben estos medios). (la conguración de traves de la red.djangoproject. puedes realizar este mismo paso distribución y balance de carga separando los servidores Web front-end y los procesos FastCGI back-end en diferentes máquinas.5. Puedes comprar balanceadores de carga por hardware caros y propietarios. puedes agregar más servidores de medios y distribuir la carga con tu cluster de balanceadores de carga. Si un servidor único de medios no es suciente. los siguientes pasos son derivaciones del último: A menida que necesites más performance en la base de datos. y inicia Apache en cada una de ellas. Con los servidores Web en cluster.com/ r/pgpool/) para replicación y pooling de conexiones. nuestra arquitectura en evolución empieza a verse más compleja. Idealmente. MySQL tiene replicación incorporada. -. como se ve en la Figura 20-4. ESCALAMIENTO 239 estáticos. no sólo incrementas capacidad.com/r/slony/) y pgpool (http://www. pero hemos encontrado que Perlbal (http://www. asumamos que el primero que se ve superado en capacidad es el servidor Web.así que si creces más allá. Observar que en el diagrama nos referimos a los servidores Web como el cluster para indicar que el numero de servidores basicamente es variable. Si un solo balanceador de carga no es suciente. Una mirada a la Figura 20-3 te permitirá cer que si falla aunque sea uno solo de los servidores. necesitarás empezar a agregar redundancia.5. De todos modos. etc. Es un balanceador de carga y proxy inverso escrito por las mismas personas que escribieron memcached (ver Capítulo 13). este paso puede ser un poco delicado. 22. necesitas otra pieza de software para distribuir el tráco entre los servidores: un balanceador de carga. Asi que a medida que agregas servidores redundantes. Si un medio habita en otro servidor.22. moverse a un servidor de medios separado es doblemente importante y debería ser el primer paso en el escalamiento hacia arriba. Sin embargo.djangoproject. Implementando Balance de Carga y Redundancia A esta altura.com/r/ perlbal/) es simplemente fantástico. videos. este servidor de medios debería correr un servidor Web desnudo.).djangoproject. el sitio entero se cae. si un cluster no tiene buena performance.

240 CAPÍTULO 22. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR Figura 22. revisión 789 del 9 de noviembre de 2009 .4: Conguración de un server redundante con balance de carga.

ESCALAMIENTO 241 Figura 22. revisión 789 del 9 de noviembre de 2009 .5.22.5: Un ejemplo de conguración de Django de gran escala.

Los procesadores más rápidos no mejoran la performance tanto. evitando el overhead de conectar y desconectar. seleccionar memcached no te hace mejor si no lo usas realmente. En cuanto empieces a swappear. La mayoría de los servidores Web desperdician el 90 % de su tiempo esperando I/O del disco. y es aun más dicil de escribir sobre eso que sobre escalamiento.6. Aceptamos diamantes en bruto y lingotes de oro. Una vez que haz llegado a este nivel. Un uso de cache agresivo y preemptico es usualmente lo único que se puede hacer para mantener un sitio funcionando bajo el mayor tráco. no hay un límite fundamental a cuantos puedes agregar. la performance directamente se muere. una arquitectura de gran escala debe verse como en la Figura 20-5. el ajuste de performance es una obligación.3. pero son mucho más caros que la RAM. Compra toda la RAM que puedas. El Capítulo 13 es tu mejor amigo aquí: aprende como usar el framework de cache de Django. así que no cuentan. Usa memcached siempre Por supuesto. La base de datos de LJWorld. Desafortunadamente.1. Ajuste de Performance Si tienes grandes cantidades de dinero. maximiza la RAM de tu servidor Web. Nota Incidentalmente.2. Si estás pensando seriamente en desplegar una aplicación Django de gran escala.6. 22. sin embargo. Esto parece bueno a primera vista. ni pierdas tiempo con los otros -. La situación ideal es aquella en la que ningún servidor swapea -nunca. Si estás planicando un sitio Django de alto tráco. si alguien con monstruosas cantidades de dinero está leyendo este libro. el primer lugar donde poner tu RAM es en el servidor de base de datos. la RAM realmente cara cuesta aproximadamente 200 dólares por gigabyte -. Esto no es tan difícil. El Apéndice A tiene alguna información proveniente de desarrolladores responsables de algunas instalaciones Django de gran escala. debes poder manejar la mayor parte del tráco normal.moneditas comparado con el tiempo empleado en ajustes de performance. Si estás sirviendo medios desde un servidor separado. es una lectura recomendada. compra suciente ram como para tener toda tu base de datos en memoria.com -. Después.ve directamente a memcached. cada usuario que esté navegando tu sitio solo requerirá una página del servidor Django cada diez segundos aproximadamente. Las siguientes secciones. A pesar de que mostramos solo dos o tres servidores en cada nivel. Usa memcached A pesar de que Django admite varios back-ends de cache diferentes. PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR Después de algunas de estas iteraciones. y un servidor HTTP ocioso consume RAM que debería estar usando un servidor activo. Si tienes un sitio con tráco alto. Los discos más rápidos pueden ayudar levemente. sin embargo. pero puede asesinar al performance de un sitio Django.4. te quedan pocas opciones. Si tienes varios servidores. 22. deberás pasar un un buen tiempo aprendiendo como ajustar cada pieza de tu stack.6. simplemente puedes irle arrojando hardware a los problemas de escalado. Si llegas a ese punto. y usalo en todas partes que te sea posible.242 CAPÍTULO 22. presentan algunos tips especícos del ajuste de performance de Django que hemos descubiero a traves de los años.que incluye medio millón de artículos desde 1989 -. el ajuste de performance es más un arte que una ciencia. Esto deja a los servidores HTTP esperando el siguiente pedido keep-alive. 22. revisión 789 del 9 de noviembre de 2009 . 22.6. No hay tal cosa como demasiada RAM Cuando escribimos esto. y después compra un poco más.tiene menos de 2 GB. Deshabilita Keep-Alive es una característica de HTTP que permite que múltiples pedidos HTTP sean servidos sobre una Keep-Alive conexión TCP única. ninguno de ellos siquiera se acerca a ser tan rápido como memcached. por favor considere una donación sustancial al proyecto Django. 22.6. Para el resto de nosotros. Si puedes.

7. 22.5.desde Linux a Apache a PostgreSQL o MySQL -. ¾QUÉ SIGUE? 243 22. ¾Qué sigue? Has alcanzado el nal de nuestro programa regular.6. únete a las comunidades open source que están detrás de tu software y pide ayuda. Tus humildes autores son solo dos miembros de un grupo increíblemente activo y creciente de desarrolladores Django. Si realmente quieres obtener ese último 1 % de tus servidores. ya sea un pequeño juguete para tí o el próximo Google. Nuestra comunidad tiene una enorme cantidad de experiencia colectiva para ofrecer. Los siguientes apéndices contienen material de referencia que puedes necesitar a medida que trabajes sobre tus proyectos Django Te deseamos la mejor de las suertes en la puesta en marcha de tu sitio Django. revisión 789 del 9 de noviembre de 2009 . Únete a la Conversación Cada pieza del stack de Django -. Y también asegúrate de unirte a la comunidad Django.7.22.tiene una comunidad maravillosa detrás. La mayoría de los miembros de la comunidad del software libre estarán felices de ayudar.

PUESTA EN MARCHA DE DJANGO EN UN SERVIDOR revisión 789 del 9 de noviembre de 2009 .244 CAPÍTULO 22.

com. categorizar y compartir información de fuentes distribuidas. Johannes Beigel es uno de los principales desarrolladores en Brainbot Technologies AG. La mayor parte de este apéndice son sus palabras. se revisión 789 del 9 de noviembre de 2009 Christian Hammond es un ingeniero senior en VMware (un desarrollador líder de software de virtual- . Está construido para el uso empresarial tanto en entornos de intranet como internet y es altamente escalable y personalizable. A. Él desarrolló Curse.1. y uno de los sitios web más grandes acerca de World of Warcraft globalmente. Ultima Online.] es una solución de software para gestionar. Nuestro sitio web se estableció a principios de 2005.y tuvimos picos de más de 130 millones de visitas (en un mes) usando Django. pero recientemente fue comprado por HewlettPackard para propósitos de mayor alcance: HP vio valor real en nuestro estilo de desarrollo web. Review Board nació como un proyecto interno de VMware. ización). nos escribimos) con un puñado de personas que tienen sitios completos y funcionales desarrollados en Django. Curse. David Trowbridge y yo discutíamos el proceso que usabamos en VMware para manejar la revisión de código. David Cramer es el desarrollador principal de Curse.com/. pero ahora es open source: A nales de 2006.. y desde nales de 2006 nos hemos estado expandiendo a juegos más allá de World of Warcraft. El desarrollo de los conceptos y componentes medulares comenzó en el 2001. Elenco Conozcamos a nuestros personajes y sus proyectos. Nos adquirieron de modo que pudiéramos llevar esa tecnología a otros sitios en la Web. y en la forma en que tendimos puentes entre el mundo virtual y el mundo físico.com.org/).com todavía es un gran sitio para contar historias. donde se pueden solicitar versiones impresas de artículos de Wikipedia. pero ahora también estamos trabajando en separar componentes y reutilizar las piezas más importantes de nuestra tecnología. Somos un sitio web muy dinámico y orientado al usuario especícamente enfocado en juegos multijugador masivos. que han sido ligeramente editadas para mayor claridad..review-board. También es el desarrollador principal de Review Board (http://www. Recientemente hemos rediseñado/reimplementado el servidor y el cliente web. hablamos (bueno.com es uno de los sitios sobre Django más grandes de Internet: Tenemos aproximadamente entre 60 y 90 millones de visitas a la página en promedio por mes.Apéndice A Casos de estudio Para ayudar a responder las preguntas acerca de cómo funciona Django en el mundo real. El equipo de Johannes actualmente está trabajando en un programa de gestión de conocimiento para empresas conocido como Brainler. que [ahora] está basado en Django. un sitio para devotos a los videojuegos multijugador masivos como World of Warcraft. y otros. Johannes nos cuenta que Brainler [. Antes de que la gente enviara código al repositorio. Tabblo. Inc. El sitio Django más orientado al público de Brainbot es http://pediapress. buscar. Tabblo comenzó su vida como una herramienta para contar historias basadas en fotos compartidas. Ned Batchelder es el ingeniero principal de Tabblo. un sistema de revisión de código basado en Web.

Comparando ambos. qué otras opciones fueron consideradas. En vez de hacer una vaga referencia a una parte del código en un email. las puse en código. nació Review Board. Todo se manejaba por email.review-board. y rápidamente comenzamos a usar el framework Twisted. el revisor puede comentar directamente sobre el código. y la gente comenzó a contribuir al proyecto en sí. Luego de investigar un poco en Internet. Pero pronto nos dimos cuenta que -. En poco tiempo. pero es la primera que hemos visto que es abierta y tiene el extenso conjunto de características en el que hemos trabajado para que incluya. mucho más rápido de lo esperado. ¾Por qué Django? Le preguntamos a cada desarrollador por qué decidió usar Django. pero pronto apreciamos todas las otras características que estaban incluidas. Review Board ayuda a los desarrolladores. El código. y cómo se tomó la decisión denitiva de usar Django. Después de algunos años de desarrollo e implementación en paralelo de sistemas (Nevow todavía está en uso para algunos proyectos en sitios de clientes). Review Board no es la única herramienta de revisión de código del mercado. Ned Batchelder : Antes de unirme a Tabblo. y por supuesto. Fue entonces que comenzamos a discutir potenciales soluciones para este problema. A. se volvió difícil seguir las revisiones que necesitaban mayor atención. nos quedó claro que Django era el framework de desarrollo web más prometedor para nuestros requerimientos. el que Django esté basado en Python signicó que tendríamos toda la riqueza del ecosistema Python para soportar nuestro trabajo. En vez de escribir mis ideas. En unas pocas semanas. Nevow era la solución más natural para resolver nuestras aplicaciones web. encontró que Django tenía una mayor profundidad técnica que haría más fácil construir un sitio robusto y escalable. que está disponible en http://www.a pesar de la integración perfecta con Twisted -. La respuesta a nuestro anuncio público fue tan im- presionante como nuestro anuncio interno a VMware. Antonio Rodriguez (fundador/CTO de Tabblo) hizo una evaluación de Rails y Django. Esto denitivamente quedó demostrado a medida que construimos Tabblo. tuvimos diez equipos usando Review Board. revisión 789 del 9 de noviembre de 2009 . Esperamos que esto repercuta en benecios de tiempo para muchos proyectos (open source o no). En poco tiempo. nuestro servidor de demostración recibió más de 600 usuarios. junto a los comentarios.2. resultando en un código mucho mejor para mantener. y todos los aspectos donde podía ahorrarnos tiempo. momento en que estabamos listos para hacer un reacondicionamiento de Curse.246 APÉNDICE A. Lo que nos condujo a Django fue la sintaxis de sus templates. Lo conversamos y al decidirnos por Django comenzamos a escribir la tercera revisio del sitio web casi inmediatamente. David Cramer : Escuché sobre Django en el verano de 2006. contribuidores y revisores a seguir la evolución del código propuesto a revisión y a mejorar la comunicación entre unos y otros. dando a los desarrolladores suciente contexto para trabajar más rápidamente en los cambios que hagan falta. y así fue que compramos rápidamente a Django. llegamos a la conclusión de que Django es mucho menos incómodo. Hicimos un anuncio de open source y habilitamos el sitio conjuntamente.org/. y encontró que ambos proveían un gran ambiente de desarrollo rápido quick-out-of-the-blocks. aparecerá entonces en las revisiones. Review Board creció rápidamente en VMware. este proyecto no es interno para VMware. Quedamos muy impresionado de lo que podía hacer. Se decidió desde el día uno que debería ser open source y estar disponible para ser usado por cualquier compañía o proyecto. Johannes Beigel : Como hemos estado codicando en Python por muchos años ya. y que es mucho más divertido para trabajar. Sin embargo. CASOS DE ESTUDIO suponía que enviaran un di de los cambios a una lista de correo para ser revisado. De hecho.muchas cosas eran un poco incómodas de realizar dentro de nuestros procesos de desarrollo ágil. También. y decidimos investigarlo.

Está basado en Python.. e intentando comprender los conceptos básicos detrás del middleware.es importante conocer en que aspectos tus armas son débiles antes de usarlas en la barricada. A. Le preguntamos a nuestro panel cómo consiguieron que su equipo adquiriese velocidad con Django y qué consejos querrían compartir con nuevos desarrolladores Django. ¾Cómo les fue? Ahora la pregunta del millón: ¾Cómo los trató Django? Estabamos especialmente interesados en escuchar dónde Django perdió fuerza -. autorización. ya que ambos han sido inestimables para nosotros. Ir de PHP a Python fue grandioso programáticamente. Tanto antes como una startup en busca del calor de clientes y negocios. Esto siempre es un problema en PHP y Perl. aunque era limitada. etiquetas de plantillas. Estábamos interesados en escuchar fue ese proceso. del que soy un gran fanático. Hemos aprendido mucho mientras desarrollábamos Review Board. como ahora como parte de HP y trabajando con un número de socios. Le aconsejaría a los nuevos usuarios que lean la tan bien escrita documentación de Django y el libro que ahora están leyendo. navegando la documentación para obtener una idea de lo que es posible (es fácil perderse muchas característica si sólo se sigue el tutorial). Christian Hammond : David y yo teníamos una experiencia previa con Django.4.½lo juramos! A. pero pronto nos dimos cuenta de que queríamos cambiar tantos aspectos conceptuales (tanto en la interfaz de usuario como en la parte del servidor de aplicación) que empezamos todo de nuevo usando el código que teníamos meramente como una referencia. los formularios. El único detalle es que hay que tener mucho más cuidado con la gestión de memoria [ya que los procesos Django permanecen mucho más tiempo que los procesos PHP (que son simples ciclos)]. David Cramer : La documentación del sitio web es grandiosa. objetos request. los otros sitios fueron portados desde código ya existente. David Cramer : El sitio anterior estaba escrito en PHP.3. no necesité pensar mucho para meterme con Django. sino que también mantiene el trabajo organizado y mantenible. nos cambiamos a Python y continuamos usando C++ para el código computacionalmente intensivo. revisión 789 del 9 de noviembre de 2009 . tuvimos que ser muy ágiles cuando hubo que adaptar el software a nuevas demandas. localización. COMENZANDO 247 Christian Hammond : Había jugado con Django en un par de pequeños proyectos y quedé muy impresionado con él. A.3.. ltros personalizados. Luego podríamos revisar más profundamente esos tópicos cuando realmente los necesitáramos.5. Basado en experiencias del pasado. y esto lo hace fácil no sólo para desarrollar sitios y aplicaciones web. [Aprendimos Django mientras] trabajamos con el tutorial. Comenzando Como Django es una herramienta relativamente nueva. Pégate a ella. Portando código existente Aunque Review Board y Tabblo fueron desarrollos desde cero. no hay muchos desarrolladores experimentados ahí afuera que lo dominen ampliamente. Johannes Beigel : Luego de programar principalmente en C++ y Perl. Ned Batchelder : Django realmente nos permitió experimentar con las funcionalidades de nuestro sitio web. modelos de base de datos. No tuvimos que sobornar a Christian para conseguir esa declaración -. Johannes Beigel : Comenzamos a migrar el sitio desde Nevow.A.

es evidente que no está construido para las necesidades especicas que cualquiera necesite. como la aplicación de autenticación. Christian Hammond : Django nos permitió construir Review Board bastante rápidamente forzándonos a estar organizados a través de la separación de URL. Una cosa que necesitó un poco de trabajo en nuestro proyecto en curso fue ajustar la conguración del archivo settings. y proveyéndonos útiles componentes listos para usar. Ahora. Los cambios introducidos (que con suerte estarán incorporados a Django en el momento que este libro vea la luz) permiten que con completa tranquilidad. hey. La primer cosa que pasó por nuestras cabezas fue. PIL. Perdimos cerca de 12 horas antes de que los servidores comenzaran a sentir el calor. y eso se aplica automáticamente cuando el servidor se inicia. El ambiente Python de trasfondo nos dio la oportunidad de utilizar bibliotecas existentes para resolver problemas sin reinventar la rueda. También hemos agregado nuestra propia facilidad para la migración de la base por lo que los desarrolladores no tienen que aplicar parches SQL para mantener los actuales esquemas de base de datos funcionando. puedan hacerlo. el modelo de desarrollo de Django te anima a basar tus modelos de objetos de datos en objetos de base de datos. vistas y plantillas. y tenemos que migrarlos desde su naturaleza asumiendo que su información se almacenará en una base. JSmin. y la abstracción de base de datos. Esto incluyó modicaciones de nuestra conguración de hardware. vistas y controladores nos brindó una modularidad que permitió elegir apropiadamente dónde extender y modicar.py y la estructura de directorios/conguración (para aplicaciones. esto de ninguna manera puede ser tan grande. Johannes Beigel : Consideramos Django como una plataforma muy satisfactoria que encaja perfectamente con la manera Pythonica de pensar. La parte más difícil del nuestro uso de Django ha sido la relación entre los objetos de memoria y lo objetos de la base de datos. La mayor parte de esto funcionó realmente bien para nosotros. como el que habían lanzado en diciembre cuando nuestro sitio fue lanzado en Django. y perfeccionar el software servidor que usábamos. etc). y BeautifulSoup son sólo un puñado de bibliotecas que agregamos para hacer algunas tareas que eran engorrosas para nosotros. Para una base de código grande y que se utilizará por mucho tiempo. cacheo. Pero con la omnipotencia del dinámico código Python.248 APÉNDICE A. revisión 789 del 9 de noviembre de 2009 . David Cramer : Gestionamos la implementación de grandes aplicaciones de base de datos en un n de semana. Primero. mayormente el hardware y el software que servía las peticiones a Django. y no podíamos continuar. con Django. fue posible hacerlo. Al tiempo del lanzamiento inicial del sitio web sobre Django. y largas noches. Segundo. deberíamos estar bien. el Mapeo Objeto-relacional (ORM) de Django no asegura que dos referencias a la misma entrada en la base de datos sean el mismo objeto Python. La pregunta surgió de nuevo: ¾Realmente era Django la mejor solución para lo que nosotros queríamos lograr? Gracias a todo el gran apoyo de la comunidad. y construyendo alguna infraestructura que soporte esas formas. CASOS DE ESTUDIO La separación de la funcionalidad en modelos. en PHP. tuvimos nuestro mayor tráco mensual del año. plantilla. donde todas las vistas de Django son métodos de algunas instancias de clase. Blizzard (los creadores de World of Warcraft) lanzaron otro parche bastante grande. A lo largo del tiempo hemos encontrado más y más usos de objetos de datos que no se ajustan a la base de datos. datos locales. que en ese entonces era lighttpd y FastCGI. Casi todo simplemente funciona según lo previsto. de algunas maneras. si soportamos el aluvión de diciembre. Esto nos hubiera llevado una o dos semanas hacerlo en el sitio web previo. Durante los meses siguientes ajustamos algunos detalles. PDFlib. pudimos implementar varios arreglos en caliente sobre el sitio durante esos días. optimización de Django. Los desarrolladores que cambian el esquema escriben una función Python para actualizar la base. aquellos (no cualquiera) que tengan que lidiar con 300 peticiones web por segundo. por lo que puedes verte en situaciones donde dos partes del código intentan modicar la misma entrada y una de las copias está anticuada. denitivamente tiene sentido gastar tiempo estudiando las formas en que tus datos serán almacenados y accedidos. aunque Django es una gran plataforma. porque implementamos un sistema altamente modular y congurable. Django ha brillado exactamente donde queríamos que lo haga. ZSI. En mayo de 2007.

un script desarrollado ad hoc. todos trabajando en la misma ocina de modo que es bastante fácil comunicarse. cinco desarrolladores. Tenemos un servidor de desarrollo. A. y cosas altamente especializadas de base de datos para nuestros componentes de búsqueda y de gestión de conocimiento. ltros y formularios son grandiosos. Postgres si los datos crecen mucho. y pruebas unitarias para casi todo. El soporte que provee Django para el uso de plantillas. A. Nos basamos en herramientas comunes como SVN y Trac. y luego agregar tantos servidores de aplicación como necesitemos para manejar la demanda. Esta es una área en la que Django no ha podido ayudarnos realmente mucho. y wiki. pero no seguimos una metodología rígida como Extreme Programming (aunque tomamos prestadas muchas ideas de ahí). ESTRUCTURA DE EQUIPO 249 Siendo un aplicación web dinámica. por lo que siempre estamos interesados en escuchar sobre ensayos y tribulaciones del mundo real. Personalmente me gustaría ver que se incorporen a Django algunas soluciones creativa para esto. Hay veces que quisiéramos usar una plantilla en particular o un ltro. Ned Batchelder : Somos un ambiente de Startup Web bastante estándar: Trac/SVN. pero no hay manera de usarlo desde JavaScript.7. Estructura de Equipo A menudo. con muchos gigas de RAM. Pound como front-end HTTPS y balanceador de carga si se necesita. Tenemos una conguración clásica: un multiplexor. Christian Hammond : Review Board tiene actualmente dos desarrolladores principales (David Trowbridge y yo) y un par de contribuidores. los proyectos son exitosos gracias a sus equipos. tuvimos que escribir un montón de código JavaScript. preferentemente Debian. un servidor de producción. y no a tecnología elegida. Ned Batchelder : Hemos usado cacheo tanto en la capa de consulta como de respuesta para agilizar los tiempos de respuesta.6.A. SQLite para pequeñas bases de datos. Eso ha funcionado bien para nosotros. porque podemos usar cacheo sobre el servidor de aplicación para evitar el acceso a la base de datos. revisión 789 del 9 de noviembre de 2009 . tanto manualmente como por pruebas de unidad. pero no son fácilmente utilizables desde código JavaScript. usamos Review Board para revisar nuestros cambios antes de incorporarlos. issue tracker. Johannes Beigel : Usamos Trac como nuestro bug tracker y wiki. Estamos hospedados en Google Code y usamos su repositorio Subversion. De hecho. Ah. Tenemos un sistema de construcción automatizada (personalizado pero basado en SCons). Consultamos a nuestro panel sobre sus equipos de trabajo. varios servidores de aplicación. Implementación Los desarrolladores de Django toman la facilidad de implementación y escalamiento muy seriamente. Somos más bien programadores pragmáticos. que intentamos incorporar en el programa. un servidor de base de datos.6. y amamos Memcached. y así. Lighttpd como servidor Web. Hacemos pruebas en nuestras computadores locales. y recientemente reemplazamos Subversion+SVK por Mercurial (un sistema de control de versiones distribuido escrito en Python que maneja la ramicación y fusión con encanto) Pienso que tenemos un proceso de desarrollo muy ágil. y qué herramientas y técnicas utilizan para permanecer en carrera. etiquetas. Johannes Beigel : Servidores Linux. y Memcached como sistema de caché. Nuestros usuarios en VMware que usan Review Board todos los días nos proveen de un montón de feedback útil y reportes de errores. David Cramer : Nuestro equipo consiste en cuatro desarrolladores web.

y actualmente Apache para el servidor Web. se le sirve una página cacheada. Allí. Block quote ends without a blank line. excepto que la maquina virtual se basa en VMware Server. El segundo servidor de producción es el de Review Board mismo. La conguración es casi idéntica al anterior. migraremos todos esos datos a un servicio similar al S3 de Amazon. Si no lo está. como archivos grandes o videos. (pero es esto actualmente): Cuando un usuario solicita el sitio se lo envía a un cluster de servidores Squid usando Lighttpd.. (actualmente) estan hospedados en un servidor corriendo una instalación mínima de Django usando lighttpd con fastcgi. También podemos mudar MySQL o Memcached a otra maquina virtual a medida que nuestra base de usuarios crece. Los datos estáticos. Tenemos varios servidores potentes que pueden escalarse cuando lo requiramos. los servidores verican si el usuario está registrado en el sistema. Uno está en VMware y consiste en una maquina virtual Ubuntu corriendo en VMware ESX.250 APÉNDICE A. donde luego cada uno hace uso de en un sistema Memcached distribuido y un bestial servidor de base de datos MySQL.. Usamos MySQL para la base de datos. revisión 789 del 9 de noviembre de 2009 . Christian Hammond : Hay dos servidores de producción en este momento. CASOS DE ESTUDIO David Cramer : Nuestra estructura todavía está para debatirse. Más adelante. Memcached para nuestro backend de cacheo. Un usuario autenticado es reenviado a un cluster de servidores corriendo Apache2 con mod_python (cada uno con una gran cantidad de memoria). unexpected unindent.

A continuación.:. porque eso ocasionaría un error de sintaxis en Python.com/documentation/0. Campos La parte más importante de un modelo -. si vas a usarla (ej. Restricciones en el nombre de los campos Django pone solo dos restricciones en el nombre de los campos: 1. más abajo.96/model-api/. where.es la lista de campos de la base de datos que dene.IntegerField() # 'pass' es una palabra reservada! 2.IntegerField() # 'foo__bar' tiene dos guiones bajos! Estas limitaciones se pueden manejar sin mayores problemas. VARCHAR). los desarrolladores de Django agregan en forma consistente nuevos atajos y conveniencias a la denición de modelos.y la única parte requerida de un modelo -. o select.djangoproject. por ejemplo: class Example(models. /0. Un nombre de campo no puede contener dos o más guiones bajos consecutivos. debido a la forma en que trabaja la sintaxis de las consultas de búsqueda de . Existe un enorme rango de opciones disponibles que no se han cubierto en otro lado. <select>). Las palabras reservadas de SQL.) se tratan en la siguiente sección.96? / B. Un nombre de campo no puede ser una palabra reservada de Python. que se usan en la interfaz de administración de Django.Model): pass = models. revisión 789 del 9 de noviembre de 2009 . son permitidas como nombres de campo.Apéndice B Referencia de la denición de modelos El Capítulo 5 explica lo básico de la denición de modelos. por ejemplo: class Example(models. Field INTEGER. El widget a usar en la interfaz de administración de Django. Ver db_column. Django usa los tipos de clase para determinar algunas cosas: El tipo de columna de la base de datos (ej. una lista completa de las clases de campo. Es buena idea chequear siempre la última documentación online en http://www. dado que Django escapea todos los nombres de tabla y columna de la base de datos en cada consulta SQL subyacente. etc. y lo utilizamos en el resto del libro. Cada campo en tu modelo debe ser una instancia de la clase de campo apropiada.. Los requerimientos mínimos de validación. Los campos de relación (ForeignKey. ordenadas alfabéticamente. como join. dado que el nombre del campo no necesariamente tiene que coincidir con el nombre de la columna en la base de datos.Model): foo__bar = models. Este apéndice explica toda opción disponible en la denición de modelos. Utiliza la sintaxis de quoteo del motor de base de datos particular.1. A pesar de que estas APIs se consideran muy estables. <input type="text">.

Esta longitud máxima es reforzada a nivel de la base de datos y en la validación de Django. B.1. que es la longitud máxima (en caracteres) del campo. No acepta CharField max_length. se requiere el argumento max_length. Observar que siempre se usa la fecha actual. REFERENCIA DE LA DEFINICIÓN DE MODELOS B.1.252 APÉNDICE B. Un EmailField que chequea que el valor sea una dirección de email válida.6. Es útil para las marcas de tiempo última modicación. Cuadro B. DateField Un campo de fecha.1. B. que revisión 789 del 9 de noviembre de 2009 . CharField requiere un argumento extra. no es un valor por omisión que se pueda sobreescribir. para cadenas cortas o largas.2: Opciones extra de FileField Argumento Descripción Una ruta del sistema de archivos local que se agregará a la conguración upload_to MEDIA_ROOT para determinar get_<fieldname>_url() de el resultado de la función de ayuda Esta ruta puede contener formato strftime (ver http://www.5.8. B. FileField Un campo de upload de archivos. no es un valor por omisión que se pueda sobreescribir.1. se agrega un campo de clave primaria automáticamente a tu modelo si no especicas una clave primaria. Un campo string. B. auto_now auto_now_add Asigna automáticamente al campo un valor igual al momento en que se crea el objeto. DateTimeField Un campo de fecha y hora. Cuadro B. TextField. DateField tiene algunos argumentos opcionales extra. Es útil para la creación de marcas de tiempo. Para grandes cantidades de texto. Tiene un argumento requerido.djangoproject. Tiene las mismas opciones extra que DateField. B.4.1. Un AutoField que se incrementa automáticamente de acuerdo con los IDs disponibles. Observar que siempre se usa la fecha actual. B.7.1. CommaSeparatedIntegerField Un campo de enteros separados por comas. BooleanField Un campo Verdadero/Falso.1. como se muestra en la Tabla B-1. usa CharField max_length.1. Normalmente no nece- IntegerField sitarás utilizarlos directamente.com/r/python/strftime/).1. como se ve en la Tabla B-3.3. Igual que en CharField.1: Argumentos opcionales extra de DateField Argumento Descripción Asigna automáticamente al campo un valor igual al momento en que se guarda el objeto. su max_length se establece automáticamente en 75.2. B.

CAMPOS 253 será reemplazada por la fecha y hora del upload del archivo (de manera que los archivos subidos no llenen el directorio dada). puedes {{object.*\. que de archivo base. El valor por omisión es False.gif porque el match se aplica al revisión 789 del 9 de noviembre de 2009 .: Opcional. será guardado en /home/media/photos/2007/01/15. obtener la URL absoluta a tu image Por ejemplo. Especica si deben incluirse todos los subdirectorios de path. no a la ruta completa (ej. FilePathField usará para ltrar los nombres de archivo. pero no con bar. Opcional.txt. El uso de un FileField o un ImageField en un modelo requiere algunos pasos: 1. MEDIA_ROOT). estos MEDIA_URL con la URL pública base de ese directorio. sin validación.3: Opciones extra de FilePathField Argumento Descripción Requerido . si dejas que cualquiera suba archivos ciegamente.gif). si tu en un plantilla con ImageField se llama mug_shot. Por supuesto.gif).1. una expresión regular como string.) Denir en este directorio. la ruta absoluta en el sistema de archivos hacia el directorio del cual este path match FilePathField debe tomar sus opciones (ej. pero no con /home/images/foo/bar. La parte ' %Y/ %m/ %d' de upload_to es formato strftime. FilePathField Un campo cuyas opciones están limitadas a los nombres de archivo en un cierto directorio en el sistema de archivos. De esta FilePathField(path="/home/images". estos argumentos pueden usarse juntos. Ver el Apéndice C para una explicación completa de estos métodos. Asegurarse de que la cuenta del usuario del servidor web tenga permiso de escritura FileField o ImageField al modelo.9. va a matchear foo23. este ejemplo: match se aplica sobre el nombre de archivo base. es necesario denir archivos no se almacenan en la base de datos. y get_FIELD_size(). match="foo. Todo lo que se va a almacenar en la base de datos es la ruta al archivo (relativa a Seguramente preferirás usar la facilidad de la función Por ejemplo. asegurándose de denir la opción para decirle a Django a cual subdirectorio de MEDIA_ROOT upload_to debe subir los archivos. tienes que prestar mucha atención a donde los estás subiendo y que tipo de archivos son. (Por performance. 3.B.: con un archivo llamado "/home/images").*". Si quieres recuperar el nombre en disco del archivo subido. 2. Por ejemplo. no la ruta completa. ' %m' es el mes en dos digitos. y upload_to es 'photos/ %Y/ %m/ %d'. get_<fieldname>_url provista por Django. o una URL que se reera a ese archivo. True o False. digamos que tu MEDIA_ROOT es '/home/media'.gif y va a matchear con /home/images/foo. En el archivo de conguración (settings). Cuadro B.txt o foo23. puedes usar los métodos get_FIELD_filename(). o el tamaño del archivo.get_mug_shot_url }}. que se muestran en la Tabla B-3. Observar que la regex será aplicada al nombre recursive "foo. Si subes un archivo el 15 de enero de 2007. get_FIELD_url(). ½No permitas que pase! B.gif bar. ' %Y' es el año en cuatro dígitos. Nota Cualquiera sea la forma en que manejes tus archivos subidos. El único 'gotcha' potencial es que manera.1. Tiene tres argumentos especiales. Valida todos los archivos subidos para asegurarte que esos archivos son lo que piensas que son. para evitar huecos en la seguridad. y ' %d' es el día en dos dígitos. Agregar el MEDIA_ROOT con la ruta completa al directorio donde quieras que Django almacene los archivos subidos. alguien podría subir un script CGI o PHP y ejecutarlo visitando su URL en tu sitio. recursive=True) nombre de archivo base (foo.txt^". a un directorio que está dentro de la raíz de documentos (document root ) de tu servidor web.

254

APÉNDICE B. REFERENCIA DE LA DEFINICIÓN DE MODELOS

B.1.10.

FloatField

Un numero de punto otante, representado en Python por una instancia de que se muestran en la Tabla B-4.

float. Tiene dos argumentos requeridos,

Cuadro B.4: Opciones extra de FloatField

Argumento

descripción
La cantidad máximo de dígitos permitidos en el número. La cantidad de posiciones decimales a almacenar con el número.

max_digits decimal_places

Por ejemplo, para almacenar números hasta 999 con una resolución de dos decimales, hay que usar:

models.FloatField(..., max_digits=5, decimal_places=2)
Y para almacenar números hasta aproximadamente mil millones con una resolución de diez dígitos decimales, hay que usar:

models.FloatField(..., max_digits=19, decimal_places=10)
B.1.11.
extra,

ImageField
Tiene dos argumentos opcionales la altura y el ancho de la imagen

Similar a

FileField, pero valida que el objeto subido sea una imagen válida. height_field y width_field, que si se utilizan, serán auto-rellenados con

cada vez que se guarde una instancia del modelo. Además de los métodos especiales también los métodos

get_FIELD_* que están disponibles para FileField, un ImageField tiene get_FIELD_height() y get_FIELD_width(). Éstos están documentados en el Apéndice C. ImageField requiere la biblioteca Python Imaging Library (http://www.pythonware.com/products/pil/).
IntegerField

B.1.12.

Un entero.

B.1.13.

IPAddressField

Una dirección IP, en formato string (ej.:

"24.124.1.30").

B.1.14.

NullBooleanField

null=True.
B.1.15.
Un

Similar a

BooleanField,

pero permite

None/NULL

como opciones. Usar éste en lugar de un

BooleanField

con

PhoneNumberField
que chequea que el valor es un teléfono válido estilo U.S. (en formato

CharField

XXX-XXX-XXXX).

Nota
Si

django.contrib.localflavor
país.

necesitas

representar

teléfonos para ver

de si ya

otros están

países, incluidas

consulta las

el

paquete para tu

deniciones

B.1.16.
Similar a

PositiveIntegerField

IntegerField,

pero debe ser positivo.

B.1.17.

PositiveSmallIntegerField

Similar a

PositiveIntegerField,

pero solo permite valores por debajo de un límite. El valor máximo permitido

para estos campos depende de la base de datos, pero como las bases de datos tienen un tipo entero corto de 2 bytes, el valor máximo positivo usualmente es 65,535. revisión 789 del 9 de noviembre de 2009

B.2. OPCIONES UNIVERSALES DE CAMPO

255

B.1.18.

SlugField 

Slug es un término de la prensa. Un slug es una etiqueta corta para algo, que contiene solo letras, números, guiones bajos o simples. Generalmente se usan en URLs. De igual forma que en Un datos.

CharField,

puedes especicar

max_length.

Si

max_length

no está especicado, Django

asume un valor por omisión de 50.

SlugField

implica

db_index=True

debido a que son los se usan principalmente para búsquedas en la base de que es una lista de campos a partir de los cuales auto-

SlugField

acepta una opción extra,

prepopulate_from,

rellenar el slug, vía JavaScript, en el formulario de administración del objeto:

models.SlugField(prepopulate_from=("pre_name", "name")) prepopulate_from
B.1.19.
Similar a no acepta nombres

DateTimeField

como argumentos.

SmallIntegerField

IntegerField, pero solo permite valores en un cierto rango dependiente de la base de datos (usualmente

-32,768 a +32,767).

B.1.20.

TextField

Un campo de texto de longitud ilimitada.

B.1.21.

TimeField

Un campo de hora. Acepta las mismas opciones de autocompletación de

DateField

y

DateTimeField.

B.1.22.

URLField

Un campo para una URL. Si la opción Como los otros campos de caracteres, omisión es 200.

verify_exists URLField

es

True

(valor por omisión), se chequea la existencia de la

URL dada (la URL carga y no da una respuesta 404). toma el argumento

max_length.

Si no se especica, el valor por

B.1.23.

USStateField

Una abreviatura de dos letras de un estado de U.S.

Nota
Si

django.contrib.localflavor
ización.

necesitas

representar

otros

países

o

estados,

busca

en

el

paquete

para ver si Django ya incluye los campos para tu local-

B.1.24.
Un requerido,

XMLField

org/)

TextField que chequea que el valor sea un XML válido que matchea con un esquema dado. Tiene un argumento schema_path, que es la ruta en el sistema de archivos a un esquema RELAX NG (http://www.relaxng. jing (http://thaiopensource.com/relaxng/jing.html)
para validar el XML.

contra el cual validar el campo.

Requiere

B.2.

Opciones Universales de Campo

Los siguientes argumentos están disponibles para todos los tipos de campo. Todos son opcionales.

revisión 789 del 9 de noviembre de 2009

256

APÉNDICE B. REFERENCIA DE LA DEFINICIÓN DE MODELOS

B.2.1.

null

Si está en

True,

Django almacenará valores vacíos como

NULL

en la base de datos. El valor por omisión es

Observar que los valores de string nulo siempre se almacenan como strings vacíos, no como

False. NULL. Utiliza null=True
solo afecta el almace-

solo para campos no-string, como enteros, booleanos y fechas. En los dos casos, también necesitarás establecer

blank=True

si deseas permitir valores vacíos en los formularios, ya que el parámetro

null

namiento en la base de datos (ver la siguiente sección, titulada  blank). Evita utilizar sin datos:

null

en campos basados en string como

razón para hacerlo. Si un campo basado en string tiene string vacío, no

NULL y el string NULL.

CharField y TextField salvo que tengas una excelente null=True, eso signica que tiene dos valores posibles para

vacío. En la mayoría de los casos, esto es redundante; la convención de Django es usar el

B.2.2.

blank

False. null. null solo se relaciona con la base de datos, mientras que blank está relacionado con la validación. Si un campo tiene blank=True, la validación del administrador de Django permitirá la entrada de un valor vacío. Si un campo tiene blank=False, es un campo requerido.
Si está en está permitido que el campo esté en blanco. El valor por omisión es Observar que esto es diferente de

True,

B.2.3.

choices

Un iterable (ej.: una lista, tupla, o otro objeto iterable de Python) de dos tuplas para usar como opciones para este campo. Si esto está dado,la interfaz de administración de Django utilizará un cuadro de selección en lugar del campo de texto estándar, y limitará las opciones a las dadas. Una lista de opciones se ve así:

YEAR_IN_SCHOOL_CHOICES = ( ('FR', 'Freshman'), ('SO', 'Sophomore'), ('JR', 'Junior'), ('SR', 'Senior'), ('GR', 'Graduate'), )
El primer elemento de cada tupla es el valor real a ser almacenado. El segundo elemento es el nombre legible por humanos para la opción. La lista de opciones puede ser denida también como parte de la clase del modelo:

class Foo(models.Model): GENDER_CHOICES = ( ('M', 'Male'), ('F', 'Female'), ) gender = models.CharField(maxlength=1, choices=GENDER_CHOICES)
o fuera de la clase del modelo:

GENDER_CHOICES = ( ('M', 'Male'), ('F', 'Female'), ) class Foo(models.Model): gender = models.CharField(maxlength=1, choices=GENDER_CHOICES)
Para cada campo del modelo que tenga establecidas

choices, Django agregará un método para recuperar el nombre

legible por humanos para el valor actual del campo. Ver Apéndice C para más detalles.

revisión 789 del 9 de noviembre de 2009

B.2. OPCIONES UNIVERSALES DE CAMPO

257

B.2.4.

db_column

El nombre de la columna de la base de datos a usar para este campo. Si no está dada, Django utilizará el nombre del campo. Esto es útil cuando estás deniendo un modelo sobre una base de datos existente. Si tu nombre de columna de la base de datos es una palabra reservada de SQL, o contiene caracteres que no están permitidos en un nombre de variable de Python (en particular el guión simple), no hay problema. Django quotea los nombres de columna y tabla detrás de la escena.

B.2.5.

db_index

Si está en

cuando ejecute

True, Django creará un manage.py syncdb).

índice en la base de datos para esta columna cuando cree la tabla (es decir,

B.2.6.

default

El valor por omisión del campo.

B.2.7.
Si es

editable

por omisión es

False, el campo no será editable en la interfaz de administración o via procesamiento de formularios. El valor True.
help_text

B.2.8.

Texto de ayuda extra a ser mostrado bajo el campo en el formulario de administración del objeto. Es útil como documentación aunque tu objeto no tenga formulario de administración.

B.2.9.
Si es campo:

primary_key

True,

este campo es la clave primaria del modelo.

Su no se especica

primary_key=True

para ningún campo del modelo, Django agregará automáticamente este

id = models.AutoField('ID', primary_key=True)
Por lo tanto, no necesitas establecer

primary_key=True

en ningún campo, salvo que quieras sobreescribir el com-

portamiento por omisión de la clave primaria.

primary_key=True

implica

blank=False, null=False,

y

unique=True.

Solo se permite una clave primaria en un

objeto.

B.2.10.

radio_admin
o tienen

ForeignKey

Por omisión,el administrador de Django usa una interfaz de cuadro de selección (<select>) para campos que son

choices.

Si

No utilice esto para un campo que no sea

radio_admin es True, Django utilizará una interfaz ForeignKey o no tenga choices.

radio-button en su lugar.

B.2.11.
Si es

unique
el valor para este campo debe ser único en la tabla.

True,

B.2.12.

unique_for_date

Asignar como valor el nombre de un valor del campo tipo fecha, por ejemplo:

DateField

o

DateTimeField

para requerir que este campo sea único para el

class Story(models.Model): pub_date = models.DateTimeField() slug = models.SlugField(unique_for_date="pub_date") ...
En este código, Django no permitirá la creación de dos historias con el mismo slug publicados en la misma fecha. Esto diere de usar la restricción no importa. revisión 789 del 9 de noviembre de 2009

unique_together

en que solo toma en cuenta la fecha del campo

pub_date;

la hora

258

APÉNDICE B. REFERENCIA DE LA DEFINICIÓN DE MODELOS

B.2.13.
Similar a

unique_for_month

unique_for_date,

pero requiere que el campo sea único con respecto al mes del campo dado.

B.2.14.
Similar a

unique_for_year

unique_for_date

y

unique_for_month,

pero para el año.

B.2.15.

verbose_name

Cada tipo de campo, excepto

ForeignKey, ManyToManyField, y OneToOneField, toma un primer argumento posi"Person's first name":

cional opcional -- un nombre descriptivo. Si el nombre descriptivo no está dado, Django lo creará automáticamente usando el nombre de atributo del campo, convirtiendo guiones bajos en espacios. En este ejemplo, el nombre descriptivo es

first_name = models.CharField("Person's first name", maxlength=30)
En este ejemplo, el nombre descriptivo es

"first name":

first_name = models.CharField(maxlength=30)
este caso hay que usar

ForeignKey, ManyToManyField, y OneToOneField requieren que el primer argumento sea una clase del modelo, en verbose_name como argumento con nombre: poll = models.ForeignKey(Poll, verbose_name="the related poll") sites = models.ManyToManyField(Site, verbose_name="list of sites") place = models.OneToOneField(Place, verbose_name="related place")
La convención es no capitalizar la primera letra del

verbose_name. Django convertirá a mayúscula automáticamente

la primera letra cuando lo necesite.

B.3.

Relaciones

Es claro que el poder de las bases de datos se basa en relacionar tablas entre sí. Django ofrece formas de denir los tres tipos de relaciones más comunes en las bases de datos: muchos-a-uno, muchos-a-muchos, y uno-a-uno. Sin embargo, la semántica de las relaciones uno-a-uno esta siendo revisada mientras se imprime este libro, así que no se cubren en esta sección. Consulte en la documentación on-line la información más actualizada.

B.3.1.

Relaciones Muchos-a-Uno

Para denir una relación muchos-a-uno, usa como un atributo de clase en tu modelo.

ForeignKey.

Se usa como cualquier otro tipo

Field:

incluyéndolo

ForeignKey Car

requiere un argumento posicional: la clase a la cual se relaciona el modelo.

Por ejemplo, si un modelo cada tiene solo un

Car tiene Manufacturer --

un

Manufacturer

-- es decir, un

Manufacturer

fabrica múltiples autos pero

usa la siguiente denición:

class Manufacturer(models.Model): ... class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer) ...
Para crear una relación recursiva -- un objeto que tiene una relación muchos-a-uno con él mismo -- usa

models.ForeignKey('self

class Employee(models.Model): manager = models.ForeignKey('self')
Si necesitas crear una relación con un modelo que aún no se ha denido, puedes usar el nombre del modelo en lugar del objeto modelo:

revisión 789 del 9 de noviembre de 2009

B.3. RELACIONES

259

class Car(models.Model): manufacturer = models.ForeignKey('Manufacturer') ... class Manufacturer(models.Model): ... models.py -- no puedes usar un string para hacer referencia a un modelo en una aplicación diferente, o hacer referencia
a un modelo que ha sido importado de cualquier otro lado. Detrás de la escena, Django agrega na Observar que de todas formas solo puedes usar strings para hacer referencia a modelos dentro del mismo archivo

"_id"

al nombre de campo para crear su nombre de columna en la base

de datos. En el ejemplo anterior, la tabla de la base de datos correspondiente al modelo

manufacturer_id.

(Puedes cambiar esto explícitamente especicando

db_column;

Car,

tendrá una colum-

ver más arriba en la sección 

db_column.) De todas formas, tu código nunca debe utilizar el nombre de la columna de la base de datos, salvo que escribas tus propias SQL. Siempre te manejarás con los nombres de campo de tu objeto modelo. Se sugiere, pero no es requerido, que el nombre de un campo

ForeignKey (manufacturer

en el ejemplo) sea el

nombre del modelo en minúsculas. Por supuesto, puedes ponerle el nombre que quieras. Por ejemplo:

class Car(models.Model): company_that_makes_it = models.ForeignKey(Manufacturer) # ...
Los campos

ForeignKey

reciben algunos argumentos extra para denir como debe trabajar la relación (ver Tabla

B-5). Todos son opcionales.

Cuadro B.5: Opciones de ForeignKey

Argumento

Descripción
Si no es

edit_inline

False,

este objeto relacionado se edita inline en la página del objeto o

relacionado. Esto signica que el objeto no tendrá su propia interfaz de administración. Usa

models.TABULAR

models.STACKED,

que designan si los objetos

editables inline se muestran como una tabla o como una pila de conjuntos de campos, respectivamente.

limit_choices_to

Un diccionario para buscar argumentos y valores (ver el Apéndice C) que limita las opciones de administración disponibles para este objeto. Usa esto con funciones del módulo

datetime de Python para limitar las opciones de fecha de los

objetos. Por ejemplo:

limit_choices_to = {'pub_date__lte': datetime.now} sólo permite la elección de objetos relacionados con pub_date
fecha/hora actual. En lugar de un diccionario, esto puede ser un objeto consultas más complejas. No es compatible con

anterior a la

Q

(ver Apéndice C) para

edit_inline. max_num_in_admin=10

max_num_in_admin

Para objetos editados inline, este es el número máximo de objetos relacionados a mostrar en la interfaz de administración. Por lo tanto, si una pizza puede tener como máximo diez ingredientes, asegurará que un usuario nunca ingresará más de diez ingredientes. Observar que esto no asegura que no se puedan crear más de diez ingredientes relacionados. Simplemente controla la interfaz de administración; no fortalece cosas a nivel de Python API o base de datos.

min_num_in_admin

La cantidad mínima de objetos relacionados que se muestran en la interfaz de administración. Normalmente, en el momento de la creación se muestran tran

num_in_admin objetos num_extra_on_change

inline , y en el momento de edición se muesobjetos en blanco además de todos los objetos

relacionados preexistentes. De todas formas, nunca se mostrarán menos de

min_num_in_admin num_extra_on_change

objetos relacionados.

La cantidad de campos en blanco extra de objetos relacionados a mostrar en el momento de realizar cambios. revisión 789 del 9 de noviembre de 2009

260

APÉNDICE B. REFERENCIA DE LA DEFINICIÓN DE MODELOS

Cuadro B.5: Opciones de ForeignKey

Argumento

Descripción
El valor por omisión de la cantidad de objetos inline a mostrar en la página del objeto en el momento de agregar.

num_in_admin raw_id_admin

Solo muestra un campo para ingresar un entero en lugar de un menú desplegable. Esto es útil cuando se relaciona con un tipo de objeto que tiene demasiadas las para que sea práctico utilizar una caja de selección. No es utilizado con

edit_inline.

related_name to_field

El nombre a utilizar para la relación desde el objeto relacionado de hacia éste objeto. Para más información, ver el Apéndice C. El campo en el objeto relacionado con el cual se establece la relación. Por omisión, Django usa la clave primaria del objeto relacionado.

B.3.2.

Relaciones Muchos-a-Muchos

Para denir una relación muchos-a-muchos, usa Por ejemplo, si una y cada

ManyToManyField. Topping

Al igual que

ForeignKey, ManyToManyField
puede estar en múltiples pizzas

requiere un argumento posicional: la clase a la cual se relaciona el modelo.

Pizza

Pizza

tiene múltiples objetos

-- es decir, un

Topping

tiene múltiples ingredientes (toppings) -- debe representarse así:

class Topping(models.Model): ... class Pizza(models.Model): toppings = models.ManyToManyField(Topping) ...
Como sucede con

ForeignKey,

una relación de un objeto con sí mismo puede denirse usando el string

'self'

en

lugar del nombre del modelo, y puedes hacer referencia a modelos que todavía no se denieron usando un string que contenga el nombre del modelo. De todas formas solo puedes usar strings para hacer referencia a modelos dentro del mismo archivo

models.py

-- no puedes usar un string para hacer referencia a un modelo en una aplicación diferente,

o hacer referencia a un modelo que ha sido importado de cualquier otro lado. Se sugiere, pero no es requerido, que el nombre de un campo

ManyToManyField (toppings,

en el ejemplo) sea un

término en plural que describa al conjunto de objetos relacionados con el modelo. Detrás de la escena, Django crea una tabla join intermedia para representar la relación muchos-a-muchos. No importa cual de los modelos tiene el en los dos.

ManyToManyField,

pero es necesario que esté en uno de los modelos -- no

Si estás usando la interfaz de administración, las instancias el

editado en la interfaz de administración. En el ejemplo anterior, los

Topping

tenga

pizzas ManyToManyField

) porque es más

ManyToManyField deben ir en el objeto que va a ser toppings están en la Pizza (en lugar de que natural pensar que una Pizza tiene varios ingredientes

(toppings) que pensar que un ingrediente está en muchas pizzas. En la forma en que está congurado el ejemplo, el formulario de administración de laPizza permitirá que los usuarios selecciones los ingredientes. Los objetos

ManyToManyField

toman algunos argumentos extra para denir como debe trabajar la relación (ver

Tabla B-6). Todos son opcionales.

Cuadro B.6: Opciones de ManyToManyField

Argumento

Descripción
El nombre a utilizar para la relación desde el objeto relacionado hacia este objeto. Ver Apéndice C para más información.

related_name filter_interface

Usa una interfaz de ltro JavaScript agradable y discreta en lugar de la menos cómoda valos debe ser

<select multiple> en el formulario administrativo de este objeto. El models.HORIZONTAL o models.VERTICAL (es decir, la interfaz ForeignKey.

debe apilarse horizontal o verticalmente).

limit_choices_to

Ver la descripción en

revisión 789 del 9 de noviembre de 2009

py startapp -. Django utilizará para más información. Las secciones que siguen presentan una lista de todas las posibles requerida. Ninguna de estas opciones es a un modelo es completamente opcional. ManyToMany con self. Si no deseas la simetría en las relaciones symmetrical db_table en False. como opciones de ordenamiento. permitiendo que las relaciones ManyToMany sean asimétricas. OPCIONES DE LOS METADATOS DEL MODELO 261 Cuadro B.esto Cuando Django procesa este modelo.. se asumen que el ManyToManyField es simétrico -. Si tu nombre de tabla de base de datos es una palabra reservada de SQL. un modelo denido como books.py startapp books). Opciones de los Metadatos del Modelo class Meta denida en el cuerpo de tu clase modelo: Los metadatos especícos de un modelo viven en una class Book(models.con el class Book books (creada por tendrá una tabla en la base de datos llamada Para sobreescribir el nombre de la tabla de la base de manage. B.Model): .CharField(maxlength=100) class Meta: # model metadata options go here . datos. entonces tu eres mi amigo.B. y como resultado. En lugar de eso. etc.4. Si no se provee.ManyToManyField("self") ManyToManyField person_set a la clase Person. con un guión bajo entre ellos. Los metadatos del modelo son cualquier cosa que no sea un campo. establece Esto forzará a Django a agregar el descriptor para la relación inversa. no agrega un atributo es. Django quotea los nombres de tabla y de columna detrás de la escena.. db_table El nombre de la tabla de la base de datos a usar para el modelo.. o contiene caracteres que no están permitidos en los nombres de variable de Python (especialmente el guión simple). Django asumirá un nombre por omisión basado en los nombres de las dos tablas a ser vinculadas. B. no hay problema. Agregar class Meta Meta opciones.Model): friends = models. class Meta: db_table = 'things_to_read' Si no se dene.1. si tienes una aplicación manage. si yo soy tu amigo. app_label + '_' + model_class_name.4. Un nombre de tabla de base de datos de un modelo se construye uniendo la etiqueta de la aplicación del modelo -..el nombre que usaste en nombre de la clase modelo.4. Considera class Person(models. Ver la sección  `Nombres de Tabla`_ revisión 789 del 9 de noviembre de 2009 . El nombre de la tabla a crear para almacenar los datos de la relación muchosa-muchos.6: Opciones de ManyToManyField Argumento Descripción Solo utilizado en la denición de el siguiente modelo: symmetrical ManyToManyField sobre sí mismo. use el parámetro db_table dentro de class Meta: class Book(models.Model): title = models. Django deriva automáticamente el nombre de la tabla de la base de datos a partir del nombre de la clase modelo y la aplicación que la contiene. Por ejemplo. identica que tiene un sobre sí mismo. Para ahorrarte tiempo.

usa esto: ordering = ['-title'. usa esto: ordering = ['title'] Para ordenar por title en orden descendente (Z-A). si una objeto Question. REFERENCIA DE LA DEFINICIÓN DE MODELOS B. Por ejemplo.2. eliminar y cambiar para cada objeto que tenga establecida la opción especica un permiso extra.4.262 APÉNDICE B. permissions Permisos extra para almacenar en la tabla de permisos cuando se crea este objeto. B. Este ejemplo revisión 789 del 9 de noviembre de 2009 . class Meta: order_with_respect_to = 'question' B. el sitio de administración usa sólo el primer campo. 'author'] Observar que no importa cuantos campos haya en ordering. del modelo.Model): order_date = models. class Meta: get_latest_by = "order_date" Ver el Apéndice C para más información sobre el método latest().CharField(maxlength=100) class Meta: ordering = ['title'] Esto es una tupla o lista de strings. Por ejemplo. harás esto: class Answer(models... Esto se utiliza casi siempre con objetos relacionados para permitir que puedan ser ordenados respecto a un objeto padre. utilizado cuando se obtienen listas de objetos: class Book(models.Model): title = models. método get_latest_by El nombre de un latest() del DateField o DateTimeField Manager del modelo. B. que indica orden "?" para ordenar al title en orden ascendente (A-Z). can_deliver_pizzas: admin.4. Los campos sin un azar. Cada string es un nombre de campo con un prejo opcional descendiente. ordering El ordenamiento por omisión del objeto. y el orden de las respuestas importa.5..3. Answer se relaciona a un y una pregunta tiene más de una respuesta. Use el string -. usa esto: ordering = ['-title'] Para ordenar por title en orden descendente.ForeignKey(Question) # . Se crean automáticamente permisos para agregar.4..DateTimeField() . order_with_respect_to Marca este objeto como ordenable con respecto al campo dado. y luego por author en orden ascendente.4. Esto especica el campo a utilizar por omisión en el Aquí hay un ejemplo: class CustomerOrder(models. para ordenar por un campo - precedente se ordenarán en forma ascendente.4.Model): question = models.

4. se incluyen las sentencias en la sentencia CREATE TABLE). class Meta: unique_together = [("department". "Can deliver pizzas"). Un Managers Manager es la interfaz a través de la cual se proveen las operaciones de consulta de la base de datos a los modelos de Django. Si no se dene.Model): .. en singular: class CustomerOrder(models. B.8. revisión 789 del 9 de noviembre de 2009 .Model): order_date = models. unique_together Conjuntos de nombres de campo que tomados juntos deben ser únicos: class Employee(models. Esta sección trata especícamente personaliza el comportamiento del Manager.. verbose_name_plural El nombre del objeto en plural: class Sphynx(models.4..4. UNIQUE apropiadas B. Existe al menos un La forma en que trabajan las clases las opciones del modelo que Manager para cada modelo en una aplicación Django..7..5. class Meta: verbose_name = "order" en camel case. Django utilizará una versión adaptada del nombre de la clase.6. Ver el Capítulo 12 para más detalles sobre permisos. class Meta: verbose_name_plural = "sphynges" Si no se dene.CharField(maxlength=10) . Django agregará una s al nal del verbose_name. B. "extension")] Esto es una lista de listas de campos que deben ser únicos cuando se consideran juntos. human_readable_permission_name). Manager está documentada en el Apéndice C. en la cual CamelCase se convierte B. MANAGERS 263 class Employee(models. verbose_name Un nombre legible por humanos para el objeto..ForeignKey(Department) extension = models.DateTimeField() .Model): department = models. ) Esto es una lista o tupla de dos tuplas de la forma (permission_code. class Meta: permissions = ( ("can_deliver_pizzas".. Es usado en la interfaz de administración de Django y se refuerza a nivel de base de datos (esto es.5..Model): .B.

execute(""" SELECT p.db import connection class PollManager(models. funciones que actúan sobre una instancia simple de un objeto modelo -.DateField() objects = PollManager() class Response(models. no B.id = r. 3 ORDER BY 3 DESC""") result_list = [] for row in cursor.append(p) return result_list class OpinionPoll(models. p. (Para funcionalidad a nivel de registro -. Nombres de Manager Por omisión.ForeignKey(Poll) person_name = models.all() devolverá una lista de todos los objetos Person. people = models.Manager): def with_counts(self): cursor = connection. por ejemplo: from django.Model): question = models. o quieres usar un nombre distinto de renombrarlo en cada uno de los modelos.people. este Manager personalizado ofrece un método with_counts(). COUNT(*) FROM polls_opinionpoll p.2.CharField(maxlength=200) poll_date = models. De todas formas. poll_date=row[2]) p. no métodos de Un método Por objetos Manager personalizados .model(id=row[0].db import models class Person(models.264 APÉNDICE B.poll_date.Model): . No tiene que retornar un QuerySet. dene un atributo de clase en ese modelo. que retorna una lista de todos los OpinionPoll. Django agrega un quieres usar de tipo objects Manager llamado objects a cada clase modelo de Django..objects generará una excepción AttributeError (dado que Person Person.5. p.Model): poll = models.TextField() revisión 789 del 9 de noviembre de 2009 . tiene un atributo objects). inicial que devuelve el Hay dos razones por las que puedes querer personalizar un para modicar el QuerySet Manager.num_responses = row[3] result_list. pero Person.esto es. REFERENCIA DE LA DEFINICIÓN DE MODELOS B.Manager() Manager objects para el Manager.fetchall(): p = self. 2. Para renombrar el models.poll_id GROUP BY 1.) Manager personalizado puede retornar cualquier cosa que necesites. ejemplo.id. puedes para una clase dada.Manager() Usando este modelo de ejemplo. Agregando Métodos Extra al Manager Agregar métodos extra al Manager es la forma preferida de agregar funcionalidad a nivel de tabla a tus modelos.question. cada uno con un atributo extra num_responses que es el resultado de una consulta agregada: from django. ciando tu Managers Personalizados Puedes utilizar un Manager Manager personalizado en un modelo en particular extendiendo la clase base Manager e instany/o personalizado en tu modelo.CharField(maxlength=50) response = models.5. polls_response r WHERE p.cursor() cursor. question=row[1].usa métodos modelo (ver más abajo)..1. si tu como nombre de campo. Manager: para agregar métodos extra al Manager.

el siguiente modelo tiene dos managers -. sobreescribiendo el método Manager. y otro que retorna solo los libros de Roald Dahl: # First. get_query_set() un QuerySet con las propiedades que tu requieres. Esta es una manera fácil de denir lters comunes para tus modelos.dahl_objects.CharField(maxlength=50) last_name = models.uno que devuelve todos los objetos. Book.CharField(maxlength=100) author = models. Por supuesto.model para obtener Otra cosa a observar en este ejemplo es que los métodos de un la clase modelo a la cual están anexados. MANAGERS 265 En este ejemplo.B. pero Book. ('F'. usando este modelo: class Book(models.count() Este ejemplo también señala otra técnica interesante: usar varios managers en el mismo modelo.get_query_set(). Modicando los QuerySets iniciales del Manager Un QuerySet base de un Manager devuelve todos los objetos en el sistema. QuerySet base. class Book(models.objects.objects. Por ejemplo. Con este modelo de ejemplo. 'Female'))) people = models.all() Book.all() retornará todos los libros de la base de datos. Puedes agregar tantas instancias de Manager() como quieras. 'Male'). Por lo tanto.dahl_objects. estas sentencias son filter().get_query_set().CharField(maxlength=50) la sentencia debe retornar Puedes sobreescribir el Book. QuerySet sobre él.filter(title='Matilda') Book. class DahlBookManager(models. puedes usar get_query_set() devuelve un objeto QuerySet. dahl_objects = DahlBookManager() # The Dahl-specific manager. choices=(('M'.dahl_objects.filter(author='Roald Dahl') # Then hook it into the Book model explicitly.CharField(maxlength=100) author = models. self). puedes usar con el atributo num_responses.Manager() # The default manager. self).Manager): def get_query_set(self): return super(MaleManager.CharField(maxlength=1.Model): title = models.Manager): def get_query_set(self): return super(FemaleManager.get_query_set().CharField(maxlength=50) objects = models. Por ejemplo. define the Manager subclass.dahl_objects.all() retornará todos los libros de la base de datos.CharField(maxlength=50) sex = models.objects.filter(sex='M') class FemaleManager(models.Manager() men = MaleManager() women = FemaleManager() revisión 789 del 9 de noviembre de 2009 .Model): first_name = models. self). y todos todas legales: Book.get_query_set().filter(sex='F') class Person(models. exclude(). OpinionPoll. Aquí hay un ejemplo: class MaleManager(models.with_counts() para retornar la lista de objetos OpinionPoll Manager pueden acceder a self.5.Manager): def get_query_set(self): return super(DahlBookManager.Model): title = models. como los otro métodos de solo retornará aquellos escritos por Roald Dahl.

all(). class Person(models.people. es altamente recomendado. este modelo tiene algunos métodos personalizados: class Person(models.birth_date < datetime. B.org/download/releases/2.CharField(maxlength=100) city = models. Mientras que los métodos deben actuar en una instancia particular del modelo.all().women.men.all(). 8.DateField() address = models..""" import datetime if datetime. En el último ejemplo. toma nota que el primer Manager que encuentre Django (en el orden en el Manager denido en una clase como el Manager por omisión. 'IN'.birth_date <= datetime. __str__ es un método mágico de Python que dene lo que debe ser devuelto si llamas a __str__() objeto. self. 1) <= self.last_name) El último método en este ejemplo es una propiedad -. 'MI'. Por ejemplo. Las propiedades son un truco ingenioso agregado en Python 2.como las del sitio de administración de Django -.""" return ' %s %s' % (self.USStateField() # Yes. Django usa str(obj) (o la función relacionada unicode(obj).Model): revisión 789 del 9 de noviembre de 2009 . 12.""" return self. particularmente como el valor mostrado para hacer el render de un objeto en el sitio de administración de Django y como el valor insertado en un plantilla cuando muestra un objeto. 'MO') @property def full_name(self): """Returns the person's full name. Si usas objetos Person.CharField(maxlength=50) state = models.usan el Manager por omisión para obtener listas de objetos. REFERENCIA DE LA DEFINICIÓN DE MODELOS Este ejemplo te permite consultar resultados predecibles.6..CharField(maxlength=50) birth_date = models. puedes leer más acerca de ellas en http://www. los métodos de modelo Dene métodos personalizados en un modelo para agregar funcionalidad personalizada a nivel de registro para tus objetos. Estos métodos se describen en las secciones que siguen.266 APÉNDICE B. Existen también un puñado de métodos de modelo que tienen un signicado especial para Python o Django. 'IA'. def baby_boomer_status(self): """Returns the person's baby-boomer status.date(1964. this is America-centric. 1): return "Pre-boomer" return "Post-boomer" def is_midwestern(self): """Returns True if this person is from the Midwest. A pesar de que esto no es requerido.2.un atributo implementado por código getter/setter personalizado.python.por lo cual es el Manager que están denidos en el modelo) tiene un status especial. str() sobre el que se describe más abajo) en varios lugares. con los Manager personalizados. por lo que generalmente es una buena idea que el primer Manager esté relativamente sin ltrar. siempre debes retornar un string agradable y legible por humanos en el Aquí hay un ejemplo: __str__ de un objeto. y Person.1. 'OH'. Por eso.date(1945. Ciertas operaciones -. Django interpreta el primer por omisión.2/descrintro/#property. Esta es una técnica valiosa para mantener la lógica del negocio en un sólo lugar: el modelo.date(1945.first_name.6.CharField(maxlength=50) last_name = models. B. 'WI'. el manager people está denido primero -. 31): return "Baby boomer" if self.Model): first_name = models. Person. 8. Métodos de Modelo Manager están pensados para hacer cosas a nivel de tabla.state in ('IL'.

B. sin repetir la información de la URL en ningún lado. De esta forma. { 'year': self. función de view. este código de plantilla es malo : get_absolute_url() get_absolute_url() en plantillas. porque sólo queremos pasar argumentos por clave.6.first_name.month. no argumentos por nombre. archive_view) puedes hacer referencia a la misma usando permalink() como sigue: @models. tu modelo puede tener un método get_absolute_url como éste: @models. en lugar de codicar en duro las URL de tus objetos. Es una buena práctica usar Por ejemplo. que te llevará directamente a la vista pública del objeto.id Django usa esto en la interfaz de administración.details'). Por ejemplo.2})/(?P<day>\d{1.CharField(maxlength=50) last_name = models. según get_absolute_url().6. como el framework de sindicación de feeds. por ejemplo: def get_absolute_url(self): return "/people/ %i/" % self.2})/$'. 'people.views. una lista de parámetros posicionales.get_absolute_url }}">{{ object. y (opcionalmente) un diccionario de parámetros por nombre.name }}</a> Pero este es bueno: <a href="{{ object.id)]) En forma similar.permalink def get_absolute_url(self): return ('archive_view'. Si un objeto dene get_absolute_url().created. Django calcula la URL completa correspondiente usando el URLconf.year. revisión 789 del 9 de noviembre de 2009 . si tienes una entrada en URLconf que se ve como esta: (r'/archive/(?P<year>\d{4})/(?P<month>\d{1. la página de edi- ción del objeto tendrá un enlace View on site. Además puedes desacoplar tus modelos de el URLconf usando el decorator permalink. 'day': self. si tu URLconf contiene una línea como ésta: (r'^people/(\d+)/$'.permalink def get_absolute_url(self): return ('people.created.day}) Observar que especicamos una secuencia vacía para el segundo argumento en este caso. MÉTODOS DE MODELO 267 first_name = models.views. get_absolute_url Dene un método get_absolute_url() para decirle a Django cómo calcular la URL de un objeto.created.2. 'month': self. Aún puedes usar el método get_absolute_url en plantillas. usan como facilidad para recompensar a las personas que han denido el método. estás estás ligando la URL absoluta del modelo a la vista que se utiliza para mostrarla.name }}</a> El problema con la forma en que simplemente escribimos get_absolute_url() es que viola levemente el principio A este decorator se le pasa DRY: la URL de este objeto de dene dos veces. self. como antes. <a href="/people/{{ object.CharField(maxlength=50) def __str__(self): return ' %s %s' % (self.last_name) B.details'.id }}/">{{ object. sustituyendo los parámetros que le has pasado en la URL. (). También un par de otras partes de Django. en el archivo URLconf y en el modelo. [str(self.

self). en lugar de agregar los parámetros directamente dentro de la SQL.connection representa la conexión actual a la base de datos.CharField(maxlength=100) tagline = models.6.268 APÉNDICE B. where. Puedes sobreescribir estos métodos para alterar el comportamiento. Un caso de uso clásico de sobreescritura de los métodos incorporados es cuando necesitas que suceda algo cuando guardas un objeto.TextField() def save(self): if self.los más notables son delete(). Esto es por consistencia y salud mental).4. B.3.TextField() def save(self): do_something() super(Blog. org/peps/pep-0249.execute() usa placeholders.execute("SELECT foo FROM bar WHERE baz = %s". Ejecutando SQL personalizado de módulo.execute(sql. Ninguna de estas opciones es requerida. Si usas esta técnica. Sobreescribiendo los Métodos por omisión del Modelo y save() Como se explica en el Apéndice C. [self. Para usarla.fetchone() o cursor. self). como en: class Admin: pass Agregar class Admin a un modelo es completamente opcional. invoconnection. tables.cursor() para obtener un objeto cursor. Ver Apéndice C. cada modelo obtiene algunos métodos automáticamente -. REFERENCIA DE LA DEFINICIÓN DE MODELOS B. que es utilizado por los enlaces Python a SQLite. puedes usar los argumentos de la API estándar de búsqueda. llama a cursor. observa que la sentencia SQL en cursor.db import connection cursor = connection.6. Python bindings. revisión 789 del 9 de noviembre de 2009 .CharField(maxlength=100) tagline = models.html).save() # Call the "real" save() method.Model): name = models. use pass.baz]) row = cursor. params WHERE personalizada. (Observar también que Django espera el placeholder Una nota nal: Si todo lo que quieres hacer es usar una cláusula y Siéntete libre de escribir sentencias SQL personalizadas en métodos personalizados de modelo y métodos a nivel " %s".fetchall() para devolver las las resultantes: def my_custom_sql(self): from django.fetchone() return row connection y cursor implementan en su mayor parte la DB-API estándar de Python (http://www. Después. y cursor. do_something_else() También puedes evitar el guardado: class Blog(models.python. la biblioteca subyacente de base de datos automáticamente agregará comillas y secuencias de escape a tus parámetros según sea necesario. no el placeholder "?". [params]) para ejecutar la SQL. Si no estás familiarizado con la DB-API de Python.Model): name = models.7. Para utilizar una interfaz de administración sin especicar ninguna opción. " %s".db. por ejemplo: class Blog(models.save() # Call the "real" save() method B. Opciones del Administrador Admin le dice a Django cómo mostrar el modelo en el sitio de administración.name == "Yoko Ono's blog": return # Yoko shall never have her own blog! else: super(Blog.cursor() cursor. La clase Las siguientes secciones presentan una lista de todas las opciones posibles de Admin. El objeto ca django.

{ 'fields': ('url'. los campos first_name y last_name se mostrarán en la misma línea: 'fields': (('first_name'. Para mostrar múltiples campos en la misma linea. Dos clases útiles denidas por la hoja de estilo del sitio de administración por omisión son revisión 789 del 9 de noviembre de 2009 . en la que cada tupla doble representa un <fieldset> en el representa el título del de campos. 'sites') }).flatpages: class FlatPage(models. y la página de la lista de cambios incluirá una navegación basada en la fecha usando ese campo. fields formulario de la página de administración. incluyendo una lista de los campos a mostrar en él. fields no está denido.. tración. donde name es un string que y field_options es un diccionario de información acerca del conjunto es una lista de tuplas dobles. Django mostrará por omisión cada campo que no sea un AutoField y tenga editable=True. 'title'. Los conjuntos de campos con el estilo wide tendrán espacio horizontal extra. conjuntos de campos con el estilo collapse y wide. class Admin: fields = ( (None. classes Un string conteniendo clases extra CSS para aplicar al conjunto de campos. Lo siguiente está tomado del modelo FlatPage que es parte de django. field_options puede tener las clave que se describen en la siguiente sección. 'city'. OPCIONES DEL ADMINISTRADOR 269 B...Model): order_date = models. { 'classes': 'collapse'. ('Advanced options'.Model): .7. (Un Las tuplas dobles conjunto de campos. 'fields' : ('enable_comments'.contrib. en el mismo orden en que aparecen los campos denidos en el modelo.) son de la forma (name. Esta clave es requerida. 'state'). Los collapse serán colapsados inicialmente en el sitio de administración y reemplazados por un pequeño enlace click to expand. Aplica múltiples clases separándolas con espacios: 'classes': 'wide extrapretty'.7. elds Establece fields para controlar la disposición de las páginas agregar y modicar de la interfaz de adminis- fields es una estructura anidada bastante compleja que se demuestra mejor con un ejemplo. 'registration_required'. class Admin: date_hierarchy = "order_date" B. El diccionario elds Una tupla de nombres de campo a mostrar en el conjunto de campos. ) Formalmente.DateTimeField() . 'address'. Aquí hay un ejemplo: class CustomerOrder(models. 'content'. en un conjunto de campos simple.7. encierra esos campos en su propia tupla.1. field_options)..B. Si <fieldset> es una sección del formulario. En este ejemplo.2. 'template_name') }). 'last_name'). date_hierarchy Establece date_hierarchy con el nombre de un DateField o DateTimeField en tu modelo.

self colored_name. Si de todas formas quieres hacer esto.3.entonces el sitio de administración B. Si no quieres 'escapear' la salida del método. B. Django hará un HTML-escape de la salida por omisión.color_code. list_display Establece __str__() Si no se dene list_display para controlar que campos se muestran en la página de la lista de list_display. dale al método un atributo True. etiquetas js Una lista de strings representando URLs de archivos JavaScript a vincular en la pantalla de administración mediante <script src="">. Django lo invocará y mostrará la salida.ADMIN_MEDIA_PREFIX. Esto puede ser usado para ajustar un tipo determinado de página de administración en JavaScript o para proveer quick links para llenar valores por omisión para ciertos campos. dale a tu modelo un método personalizado. No se admiten los campos ManyToManyField.esto es.4. con la representación de cada objeto. porque eso implicaría la ejecución de una sentencia SQL list_display. 'decade_born_in') def decade_born_in(self): return self. Django mostrará unos bonitos iconos on o Si el string dado es un método del modelo.270 APÉNDICE B."> %s %s</span>' % (self. y debes crear las secuencias de escape correspondientes para cualquier carácter especial HTML (como los ampersands).short_descripción = 'Birth decade' Si el string dado es un método del modelo.) o separada para cada la en la tabla. Se usa tal cual es.7. 'colored_name') def colored_name(self): return '<span style="color: # %s. Si usas URLs relativas -.allow_tags = True revisión 789 del 9 de noviembre de 2009 . URLs que no empiezan con prejará automáticamente estos enlaces con http:// o / settings.Model): first_name = models. Aquí está un modelo de ejemplo completo: class Person(models. REFERENCIA DE LA DEFINICIÓN DE MODELOS description Un string de texto extra opcional para mostrar encima de cada conjunto de campos.CharField(maxlength=6) class Admin: list_display = ('first_name'.strftime(' %Y')[:3] + "0's" decade_born_in.7.DateField() class Admin: list_display = ('name'.Model): name = models. el sitio de administración mostrará una columna simple list_display: __str__() del administrador. y agrega el nombre de ese método a personalizados en Si el campo es un o  en lugar de list_display BooleanField True o False.birthday. 'last_name'. (Más información sobre métodos en breve. Aquí hay algunos casos especiales a obsevar acerca de Si el campo es una ForeignKey. Django mostrará el del objeto relacionado.first_name.CharField(maxlength=50) last_name = models.CharField(maxlength=50) birthday = models. de manera que puedes usar cualquier HTML. self. NullBooleanField. bajo el encabezado del mismo.CharField(maxlength=50) color_code = models. Este método debe tener un atributo de función short_description para usar como encabezado del campo. allow_tags con el valor en Aquí está un modelo de ejemplo completo: class Person(models. -.

por lo cual está perfectamente OK hacer esto: list_display = ('__str__'. Establece list_display_links a una lista o tupla de nombres de campo (en el mismo formato que list_display) para vincularlos. la página de la lista de cambios vinculará la primera columna -. revisión 789 del 9 de noviembre de 2009 .CharField(maxlength=50) color_code = models. 'last_name'.7.CharField(maxlength=6) class Admin: list_display = ('first_name'.7.a la página de cambios de cada ítem. 'birthday') list_display_links = ('first_name'. class Admin: list_display = ('first_name'. debes denir también list_display. debes denir list_display. a Django no le preocupa si los campos vinculados son muchos o pocos.color_code. Pero list_display_links te permite cambiar cuáles columnas se vinculan. list_display_links Establece list_display_links para controlar cuales campos de list_display deben ser vinculados a la pagina de cambios de un objeto. En este ejemplo. list_display -."> %s</span>' % (self. De todas formas. list_display_links puede especicar uno o varios nombres de campo.CharField(maxlength=50) birthday = models.B.strftime(' %Y')[:3] == 5 born_in_fifties. El único requerimiento es que si quieres usarlist_display_links. self.Model): first_name = models. mostrará un bonito class Person(models..boolean = True Los métodos __str__() son tan válidos en list_display como cualquieras otro método del modelo. 'colored_first_name') def colored_first_name(self): return '<span style="color: # %s.first_name) colored_first_name. observa que para usar Por omisión.Model): first_name = models.admin_order_field = 'first_name' El código precedente le dirá a Django que ordene según el campo por colored_first_name first_name cuando trate de ordenar en el sitio de administración. si un elemento de indicar este hecho estableciendo el atributo list_display representa cierto campo de la base de datos.DateField() class Admin: list_display = ('name'. 'born_in_fifties') def born_in_fifties(self): return self. OPCIONES DEL ADMINISTRADOR 271 Si el string dado es un método del modelo que retorna icono on o o  si le das al método un atributo Aquí está un modelo de ejemplo completo: True boolean con o valor en False.birthday. puedes admin_order_field del ítem. los elementos de list_display que no son campos de la base de datos no pueden ser utilizados en ordenamientos (porque Django hace todo el ordenamiento a nivel de base de datos).Model): . B. Django True.el primer campo especicado en list_display_links..allow_tags = True colored_first_name.5. 'last_name') Finalmente. Mientras los nombres de campo aparezcan en list_display. por ejemplo: class Person(models. los campos first_name y last_name serán vinculados a la página de la lista de cambios: class Person(models. 'some_other_field') Usualmente.

los botones de guardado aparecen solamente al pie de los formularios. list_per_page Establece list_per_page para controlar cuantos items aparecen en cada página de la lista de cambios del admin- istrador. o ForeignKey.8.9.7. Esto debe ser una lista o tupla en el mismo formato que el parámetro ordering del modelo. list_display list_filter: class User(models. Por omisión. Normalmente. los objetos tienen tres opciones al guardar: Save.7. Si estableces los botones aparecerán en el encabezado y al pié del formulario. B. B.272 APÉNDICE B. 'first_name'. Por omisión es False. list_lter Establece los tipos y list_filter para activar los ltros en la barra lateral derecha de la página de la lista de cambios en la interfaz de administración. salvo que uno de los campos list_display sea una Para más detalles sobre select_related().contrib. B. Esto puede ahorrarte una cantidad de consultas a la base de datos si estás utilizando objetos relacionados en la lista de cambios que muestra el administrador.. False. la interfaz de administración de Django usará el ordenamiento por omisión del modelo. save_as False.models.7. list_select_related Establece list_select_related para indicarle a Django que use select_related() al recuperar la lista de objetos True o de la página de la lista de cambios del administrador. Save as signica que el objeto será guardado como un objeto nuevo (con un identicador nuevo). DateTimeField. ver Apéndice C. Debe ser una lista de nombres de campo que se utilizará para la búsqueda cuando alguien envíe una consulta en ese cuadro de texto.7.User muestra como trabajan ambos. class Admin: list_display = ('username'. Estos campos deben ser de alguna tipo de campo de texto. Por omisión. como una búsqueda relacionada sobre una ForeignKey CharField o TextField.7. DateField. Debe ser una lista de nombres de campo. 'is_staff') list_filter = ('is_staff'.12.7. REFERENCIA DE LA DEFINICIÓN DE MODELOS B.6.7. save_as a True para habilitar la característica save as es en los formularios de cambios del administrador. y cada campo especicado debe ser de alguno de Este ejemplo. Normalmente.10.7.11. del administrador. Si no está denido. este valor se establece en 100.. tomado del modelo BooleanField. 'is_superuser') B. B. en lugar del Por omisión. search_elds Establece search_fields para habilitar un cuadro de búsqueda en la página de la lista de cambios del admin- istrador. Save and continue editing y Save and add save_as True. es Save and add another será reemplazado por un botón Save as. También puedes realizar con la notación de búsqueda de la API: revisión 789 del 9 de noviembre de 2009 . B. save_on_top es False. save_on_top Establece save_on_top para agregar botones de guardado a lo largo del encabezado de tus formularios de cambios save_on_top. save_as Establece another. El valor debe ser ForeignKey. Si objeto viejo. 'last_name'. 'email'. ordering Establece ordering para especicar como deben ordenarse los objetos en la página de la lista de cambios del administrador.Model): .auth. django.

Además. Django hará el equivalente a esta clausula WHERE en SQL: WHERE (first_name ILIKE 'john' OR last_name ILIKE 'john') AND (first_name ILIKE 'lennon' OR last_name ILIKE 'lennon') Observar que la entrada de la consulta se divide por los espacios... Cuadro B. pero usa un índice. si la columna tiene un índice. Django hará el equivalente a esta cláusula WHERE en SQL: WHERE (first_name ILIKE 'john %' OR last_name ILIKE 'john %') AND (first_name ILIKE 'lennon %' OR last_name ILIKE 'lennon %') Esta consulta es más eciente que la consulta ' %john %'.7: Operadores Permitidos en search_elds Operador Signicado el principio del campo. sin distinguir mayúsculas y minúsculas. dado que la base de datos solo necesita Matchea examinar el principio de una columna de datos. '=last_name'] y un usuario busca john lennon. Por ejemplo. = Matchea exactamente. '^last_name']. Django hará el equivalente a esta cláusula WHERE WHERE (first_name ILIKE ' %john %' OR last_name ILIKE ' %john %') AND (first_name ILIKE ' %lennon %' OR last_name ILIKE ' %lennon %') Para búsquedas más rápidas y/o más restrictivas. 'last_name'] search_fields. Actualmente solo está disponible para MySQL. agrega como prejo al nombre de campo un operador como se muestra en la Tabla B-7. 'john winston' @ first_name es exactamente Realiza una búsqueda en todo el texto.7.B.Model): department = models. Django divide la consulta de búsqueda en palabras y retorna todos los objetos que contengan alguna de las palabras. por lo cual actualmente no es posible hacer una búsqueda de todos los registros en los cuales (con un espacio en el medio).ForeignKey(Department) . algunas bases de datos pueden permitir el uso del índice para esta consulta. class Admin: search_fields = ['department__name'] Cuando alguien hace una búsqueda en el cuadro de búsqueda del administrador. donde cada palabra debe estar en al menos uno de los y un usuario busca en SQL: ['first_name'. y un usuario busca john lennon. si search_fields es john lennon. OPCIONES DEL ADMINISTRADOR 273 class Employee(models. a pesar de que sea una consulta ^ LIKE. Por ejemplo. en lugar de buscar a través de todos los datos de la columna. si search_fields es ['^first_name'. Por ejemplo. sin distinguir mayúsculas y minúsculas. revisión 789 del 9 de noviembre de 2009 . Es similar al método de búsqueda predeterminado. si search_fields es ['=first_name'.

274 APÉNDICE B. REFERENCIA DE LA DEFINICIÓN DE MODELOS revisión 789 del 9 de noviembre de 2009 .

ManyToManyField(Author) def __str__(self): return self.ForeignKey(Blog) headline = models. save() Creando Objetos para grabarlo en la base de datos: Para crear un objeto. De manera similar a lo que ocurre con las APIs de modelos descriptos en el apéndice B. Es buena idea consultar siempre la documentación en línea más actual que está disponible en com/documentation/0.CharField(max_length=255) body_text = models. A lo largo de este apéndice. estas APIs son considerados muy estables. tagline='All the latest Beatles news. vamos a hacer referencia a los siguientes modelos.Model): blog = models. crea una instancia de la clase modelo usando argumentos de palabra clave y luego llama a >>> from mysite.1.TextField() pub_date = models.EmailField() def __str__(self): return self. Una vez que hayas denido un modelo.') >>> b.TextField() def __str__(self): return self. usarás esta API en cualquier momento que necesites acceder a la base de datos.djangoproject.name class Author(models.save() revisión 789 del 9 de noviembre de 2009 .models import Blog >>> b = Blog(name='Beatles Blog'. los cuales pueden formar una simple from django. aplicación de blog: http://www.DateTimeField() authors = models.name class Entry(models.Model): name = models.CharField(max_length=100) tagline = models.db import models class Blog(models. Has visto ejemplos del uso de esta API a través del libro.blog.CharField(max_length=50) email = models.headline C.Apéndice C Referencia de la API de base de datos La API de base de datos de Django es la otra mitad de la API de modelos discutido en el Apéndice B.Model): name = models.96/db-api/. este apéndice explica todas las varias opciones detalladamente. aunque los desarrolladores de Django constantemente añaden nuevos atajos y conveniencias.

save() >>> b2.1. DateFields datetime Python para almacenar datos. tagline='Thoughts on cheese. Se le solicita a cada campo que provea su valor actual en un tipo de dato que puede ser grabado en la base de datos. a cada modelo se le da una clave primaria autoincremental llamada id a menos que explícitamente Apéndice B). Se le solicita a cada campo del objeto implementar cualquier modicación automatizada de datos que pudiera necesitar realizar. 14 No hay forma de saber cual será el valor de un identicador antes que llames a es calculado por tu base de datos. None >>> b2. ese valor incrementado automáticamente será atributo de tu objeto la primera vez que llames a save(): >>> b2 = Blog(name='Cheddar Talk'. Django no accede a la base de datos hasta que tú no retorna nada. detrás de escena. name='Cheddar Talk'. Pre-procesar los datos. 3.276 APÉNDICE C. Claves primarias autoincrementales especiques primary_key=True en el campo (ver la sección titulada  AutoField en el Si tu modelo tiene un AutoField. están listos para escribir como un objeto de Python. Por ejemplo. save() save(). Preparar los datos para la base de datos. revisa la documentación en línea para obtener la información más actual.id b3. tagline='Thoughts on cheese. pre_save. Las bases de datos no almacenan objetos de manera que el valor del campo debe ser convertido en una cadena de fecha que cumpla con la norma ISO correspondiente para la inserción en la base de datos. because b doesn't have an ID yet. Puedes registrar un Emitir una señal pre_save. Esto provee una noticación de que un objeto está a punto de *listener* que será invocado en cuanto esta señal sea emitida. Insertar los datos en la base. De nuevo. como enteros y cadenas. 5. ser grabado. Si un modelo tiene un identicador: Por conveniencia. Los datos pre-procesados y preparados son entonces incorporados en una sentencia SQL para su inserción en la base de datos. calculado y grabado como un save() esto se debe a que ese valor AutoField pero quieres denir el identicador de un nuevo objeto explícitamente cuando grabas. 2. REFERENCIA DE LA API DE BASE DE DATOS Esto. 4. tipo de datos más complejos requieren a menudo alguna modicación. solo defínelo explícitamente antes de grabarlo en vez de conar en la asignación automática de valor del >>> >>> 3 >>> >>> 3 b3 = Blog(id=3. INSERT. como campos de archivo.id # Returns the ID of your new object.') b3. Para crear un objeto y grabarlo todo en un paso revisa el método create de la clase Manager que describiremos C. Como con la señal no han sido documentadas. estas señales todavía C. esta es utilizada para proporcionar noticación de que un objeto ha sido grabado satisfactoriamente. Emitir una señal post_save.1.save() b3.id revisión 789 del 9 de noviembre de 2009 . Estas señales todavía están en desarrollo y no estaban documentadas cuando este libro fue a impresión.1. Qué pasa cuando grabas? Cuando grabas un objeto.los datos del campo se guardan tal como están. Sólo se usa pre-procesamiento en campos que tienen comportamiento especial. ejecuta una sentencia SQL explícitamente invoques a El método en breve. La mayoría de los campos no realizan pre-procesamiento -. La mayoría de los campos no requieren preparación de los datos.id # Returns None. Los tipos de datos simples. usa un objeto datetime. no por Django.2.') >>> b2. Django realiza los siguientes pasos: 1. Sin embargo.

save() Detrás de escena. Django ejecuta una consulta UPDATE.2.') >>> b4. Si el atributo clave primaria del objeto no tiene valor o si lo tiene pero no existe un registro. cuando estás seguro de que no tendrás colisiones de claves primarias. Django abstrae la necesidad de usar sentencias SQL Especícamente. Grabando cambios de objetos save(). tagline='Anything but cheese. esto ejecuta una sentencia SQL que llamas explícitamente a save().2.objects. este ejemplo cambia su nombre y actualiza Para grabar los cambios hechos a un objeto que existe en la base de datos. Recuperando objetos A través del libro has visto cómo se recuperan objetos usando código como el siguiente: >>> blogs = Blog. De nuevo: Django no accede a la base de datos hasta Como sabe Django cuando usar UPDATE y cuando usar INSERT save() para INSERT o UPDATE.filter(author__name__contains="Joe") Hay bastantes partes móviles detrás de escena aquí: cuando recuperas objetos de la base de datos.author = joe >>> entry. True Habrás notado que los objetos de base de datos de Django usan el mismo método crear y cambiar objetos. estás construyendo realmente un solicitados. Django asumirá que estás cambiando el registro existente en vez de crear uno nuevo. un valor distinto a consulta SELECT None o a la cadena vacía) Django ejecuta una para determinar si existe un registro con la clave primaria es- pecicada.C. Django ejecuta un INSERT. Dado el ejemplo precedente de blog datos: 'Cheddar Talk'. C.create(name="Joe") >>> entry. C. Blog b5 que ya ha sido grabada en la base de datos. GRABANDO CAMBIOS DE OBJETOS 277 Si asignas manualmente valores de claves primarias autoincrementales ½Asegúrate de no usar un valor de clave primaria que ya existe!. Si el registro con la clave primaria especicada ya existe. Si creas un objeto con un valor explícito de clave primaria que ya existe en la base de datos. Este QuerySet sabe como ejecutar SQL y retornar los objetos . Django sigue este algoritmo: Si el atributo clave primaria del objeto tiene asignado un valor que evalúa (esto es.name = 'New name' >>> b5.save() # Overrides the previous blog with ID=3! El especicar explícitamente valores de claves primarias autoincrementales es más útil cuando se están grabando objetos en lotes.3. simplemente asigna un objeto del tipo correcto al campo en cuestión: >>> joe = Author. debes tener cuidado de no especicar un valor explícito para una clave primaria cuando grabas nuevos objetos si es que no puedes garantizar que el valor de clave primaria está disponible para ser usado.objects.save() Django se quejará si intentas asignar un objeto del tipo incorrecto. revisión 789 del 9 de noviembre de 2009 QuerySet usando el Manager del modelo. UPDATE. usa Dada la instancia de su registro en la base: >>> b5. La actualización de campos ForeignKey funciona exactamente de la misma forma. name='Not Cheddar'. Debido a esto. cuando llamas a save(). este ejemplo sobrescribiría el registro previo en la base de >>> b4 = Blog(id=3.

Por los evaluará. in <module> AttributeError: Manager isn't accessible via Blog instances. usualmente solo necesitarás seleccionar un subconjunto del conjunto completo de objetos. porque se podría haber agregado o borrado un Entry durante el pequeñísimo período de tiempo entre ambas peticiones. El todos los objetos de la tabla de base de datos del modelo.4.manager. Por ejemplo. así: criterios que limitan la colección basados en parámetros provistos. Consigues un QuerySet usando el Manager del modelo. C.Django graba el resultado de la consulta en el cache del QuerySet y retorna los resultados que han sido solicitados explícitamente (por ejemplo.objects <django.objects. o muchos ltros -- >>> Blog. ahora vamos a ver cómo funcionan. Usualmente harás esto usando los >>> y2006 = Entry. por lo tanto.objects.all()] print [e.Manager object at 0x137d00d> Los Managers solo son accesibles a través de las clases de los modelos. REFERENCIA DE LA API DE BASE DE DATOS El Apéndice B trató ambos objetos desde el punto de vista de la denición del modelo. print [p.5.objects.all()] Eso signica que la consulta sera ejecutada dos veces en la base de datos.db. en vez desde una instancia de un modelo. tagline='Bar') >>> b. usa el método Manager: all() >>> Entry.all() El método retorna un QuerySet de todos los objetos de la base de datos. revisión 789 del 9 de noviembre de 2009 . el nombre objects.y. QuerySet es evaluado -. C. por omisión. renas el métodos filter() y/o QuerySet exclude(): inicial. Para crear tal subconjunto. En términos de SQL un una declaración QuerySet representa una colección de objetos de tu base de datos. Un QuerySet se compara a SELECT y un ltro es una cláusula de limitación como por ejemplo WHERE o LIMIT. para así hacer cumplir con la separación entre las operaciones a nivel de tabla y las operaciones a nivel de registro: >>> b = Blog(name='Foo'.pub_date for p in queryset] # Reuse the cache from the evaluation.pub_date for e in Entry. para minimizar el acceso a la base de datos. para escribir código mas eciente.headline for p in queryset] # Evaluate the query set. Es importante entender como recién creado. Para hacer esto. duplicando la carga sobre la misma. QuerySets correctamente. Ten presente este comportamiento de caching. all() Filtrando objetos en un La manera mas simple de recuperar objetos de una tabla es conseguirlos todos. simplemente graba el QuerySet y re-úsalo: queryset = Poll. Manager es la principal fuente de QuerySets para un modelo. el siguiente elemento. y los descartará: print [e. Cada modelo tiene por lo menos un Manager y tiene.headline for e in Entry. uno. Puede tener cero.objects.exclude(pub_date__year=2006) Tanto filter() como exclude() toman argumentos de patrones de búsqueda. lo siguiente creará dos QuerySets.objects Traceback (most recent call last): File "<stdin>".objects.filter(pub_date__year=2006) >>> not2006 = Entry. Cada En un Caching y QuerySets QuerySet QuerySet contiene un cache. ocurre un acceso a la base de datos -. line 1.objects.all() print [p. También existe una posibilidad de que las dos listas pudieran no incluir los mismos registros de la base de datos. Para evitar este problema. los cuales se discutirán detallada- mente en breve. porque puede morderte si no usas tus ejemplo. el cache esta vacío. La primera vez que un funciona.models. si se está iterando sobre el QuerySet). Actúa como un QuerySet raíz que describe todos Blog. añadiendo condiciones con ltros. Accede al mismo directamente a través de la clase del modelo.objects es el QuerySets inicial que contiene los objetos Blog en la base de datos. Evaluaciones subsecuentes del QuerySet re-usan los resultados alojados en el cache. Sin embargo.278 APÉNDICE C.

con un criterio adicional que excluye los registros cuyo registros cuyo hoy.filter(pub_date__year=2006) qs = qs. Por ejemplo.now()) Estos tres QuerySets son separados. Convirtiendo a una lista : Puedes forzar la evaluación de un mismo.now()) q3 = q1. 1.all()) Sin embargo.5.5. puedes enlazar/encadenar ltros todo el día y Django no ejecutará realmente la consulta hasta que el QuerySet sea evaluado. Esto es por con- veniencia en el interprete interactivo Python.filter(headline__startswith="What") q2 = q1.exclude(pub_date__gte=datetime.filter(headline__startswith='What') >>> qs = qs. pero Django ejecutará la consulta a la base de datos si usas el parámetro step de la sintaxis de rebanado. por ejemplo: >>> qs = Entry. De hecho. Cada renamiento crea un ser almacenado. y ejecuta su consulta en la base de datos la primera vez que iteras QuerySet no es evaluado hasta que se iterado sobre él en el bucle qs = Entry. Rebanado : Según lo explicado en la próxima sección  Limitando QuerySets. QuerySet son perezosos -.objects.datetime(2005. quedas advertido de que esto podría signicar un gran impacto en la memoria porque Django cargará cada elemento de la lista en memoria. las tres líneas precedentes no hacen ninguna llamada a la base de datos. Usualmente el rebanar un QuerySet QuerySet puede ser (no evaluado).filter(pub_date__gte=datetime. El segundo es un sub-conjunto del primero. un retorna otro QuerySet rebanado usando la sintaxis de rebanado de arreglos de Python.datetime. el iterar sobre un los mismos. agrega un ltro. FILTRANDO OBJETOS 279 C.C.objects. el siguiente for: QuerySet es iterable. El primero es un QuerySet base que contiene todas las entradas que contienen un título que empieza con What. por ejemplo: QuerySet ejecutando list() sobre el >>> entry_list = list(Entry. usado y re-usado: QuerySet que no está de ninguna manera QuerySet separado y distinto que puede q1 = Entry.exclude(pub_date__gte=datetime.1. y el día actual. así puedes ver inmediatamente tus resultados cuando usas el API interactivamente. QuerySet sacará ventaja de tu base de datos para cargar datos e inicializar objetos solo a medida que vas necesitando Los QuerySets ltrados son únicos Cada vez que renas un QuerySet obtienes un nuevo atado al QuerySet` anterior. 2005. Encadenando ltros El resultado de renar un QuerySet es otro QuerySet así que es posible enlazar renamientos. y luego otro ltro.objects. El resultado nal es un Es importante precisar aquí que los QuerySet conteniendo todas las entradas con un título que empieza con What que fueron publicadas entre Enero 1.filter(headline__icontains="bill") for e in qs: print e.objects. 1)) Esto toma el QuerySet inicial de todas las entradas en la base de datos. revisión 789 del 9 de noviembre de 2009 . Puedes evaluar un QuerySet en cualquiera de las siguientes formas: Iterando : Un sobre el.filter(pub_date__gte=datetime. luego una exclusión. Imprimiéndolo : Un QuerySet es evaluado cuando ejecutas repr() sobre el mismo. El tercero es un sub-conjunto del primero.el acto de crear un QuerySet no implica ninguna actividad en la base de datos.now()) >>> qs = qs. con un criterio adicional que selecciona solo los pub_date es mayor que el día de inicial (q1) no es afectado por pub_date es mayor que el día de hoy. En cambio.headline Esto imprime todos los títulos desde el 2006 que contienen bill pero genera solo un acceso a la base de datos. El QuerySet el proceso de renamiento.

Una excepción es si >>> Entry. aleatoriamente. sin embargo. distinct(). Métodos de consulta que retornan nuevos QuerySets Django provee una variedad de métodos de renamiento de retornados por el mas adelante.get() Nota.objects. así: >>> Entry. Algunos de estos métodos reciben argumentos de patrones de búsqueda. esto retorna el primer alfabéticamente por título: SELECT foo FROM bar LIMIT 1) usa un simple índice Entry en la base de datos. Estos métodos se describen en las secciones que siguen. en vez de un rebanado. IndexError mientras el segundo generará DoesNotExist si C.all() no introducen la posibilidad de registros duplicados. Esto 5): 5 LIMIT 5): -. esto retorna las primeras cinco entradas (LIMIT LIMIT y OFFSET.objects.all()[:5] Esto retorna las entradas desde la sexta hasta la décima (OFFSET >>> Entry.filter(pub_date__year=2005). luego por headline de forma ascendente.all()[5:10] Generalmente. Puedes sobrescribir esto para una consulta particular usando el método order_by(): >>> Entry. consultas simples como QuerySet no eliminará las duplicadas.5.objects. Esto elimina las duplicadas en el problema porque resultado de la misma. es posible obtener resultados duplicados cuando un es evaluado.order_by('-pub_date'. los resultados retornados por un por la opción ordering QuerySet están ordenados por la tupla de ordenamiento indicada en los metadatos del modelo (ver Apéndice B). después de ordenar las entradas >>> Entry. que el primero de estos generará ninguno de los objetos coincide con el criterio dado. order_by(*campos) Por omisión. QuerySet a un cierto número de resultados. Limitando QuerySets Usa la sintaxis de rebanado de arreglos de Python para limitar tu es equivalente a las clausulas de SQL de Por ejemplo.5. Por ejemplo. El "-pub_date" indica orden descendiente.objects. el rebanar un QuerySet retorna un nuevo QuerySet usas el parámetro step de la sintaxis de rebanado de Python. Sin embargo.order_by('headline')[0] y es equivalente a lo siguiente: >>> Entry. un QuerySet que usa SELECT DISTINCT en su consulta SQL. exclude(**kwargs) Retorna un nuevo QuerySet conteniendo objetos que no son iguales a los parámetros de búsqueda provistos. En la práctica esto raramente es un Blog. QuerySet QuerySet que modican ya sea los tipos de resultados o la forma como se ejecuta su consulta SQL.2. esto realmente ejecutaría la consulta con el objetivo de retornar una lista.objects. 'headline') Este resultado será ordenado por signo negativo en frente de Para ordenar pub_date de forma descendente.no evalúa la consulta.objects.all()[:10:2] Para recuperar un solo objeto en vez de una lista (por ej. si tu consulta abarca múltiples tablas.order_by('headline')[0:1]. objeto de por medio de los primeros diez: >>> Entry. REFERENCIA DE LA API DE BASE DE DATOS C.280 APÉNDICE C.objects. Por ejemplo.objects.3. los cuales se discuten en detalle lter(**lookup) Retorna un nuevo QuerySet conteniendo objetos que son iguales a los parámetros de búsqueda provistos.esta ausente se asume un orden ascendente. Si el . usa "?".order_by('?') distinct() Retorna un nuevo Por omisión. Esos son los casos en los que usarías QuerySet revisión 789 del 9 de noviembre de 2009 .

objects. month o day. 1). 20). *campos. Es más eciente el seleccionar solamente los campos que necesitas usar. 'tagline': 'All the latest Beatles news. >>> Blog. 20).dates('pub_date'. Cada objeto datetime.datetime en la lista de resultados es truncado de especial que evalúa a una lista de objetos retorna una lista de todos los valores de años distintos entre sí para el campo. El mismo especica cómo ordenar los resultados.filter(headline__contains='Lennon'). Si no especicas los campos. 2. 'day'. 'tagline': 'All the latest Beatles news.datetime(2005. 'name': 'Beatles Blog'.dates('pub_date'. datetime.datetime(2005.objects. Si especicas los campos. 'name') [{'id': 1. 2. dates(campo. 'month') [datetime.datetime(2005. Aquí tenemos algunos ejemplos: >>> Entry. datetime. tipo. order='DESC') [datetime. los cuales especican los nombres de campos SELECT.filter(name__startswith='Beatles') [Beatles Blog] # This list contains a dictionary. 20)] >>> Entry. 3. 1. cada diccionario contendrá solamente las claves/valores de campos para los campos que especiques.datetime que representan todas las QuerySet.objects. cada diccionario contendrá una clave y un valor para todos los campos en la table de base de datos: >>> Blog. >>> Blog. 'name': 'Beatles Blog'}] Este método es útil cuando sabes de antemano que solo vas a necesitar valores de un pequeño número de los campos disponibles y no necesitarás la funcionalidad de un objeto instancia de modelo. El argumento sea year. datetime.objects. 3. debe ser 'ASC' o 'DESC'.values() [{'id': 1. 'day') [datetime. 2. 20)] >>> Entry. orden.datetime(2005.values('id'. 'year') [datetime.filter(name__startswith='Beatles').datetime(2005. orden) Retorna un QuerySet fechas disponibles de un cierto tipo en el contenido de la tipo El argumento debe ser ya acuerdo al tipo datetime.objects.C. campo debe ser el nombre de un DateField o de un DateTimeField de tu modelo. provisto: "year" "day" "month" retorna una lista de todos los valores de años/mes/día distintos entre sí para el campo.dates('pub_date'. retorna una lista de todos los valores de años/mes distintos entre sí para el campo.objects. con las las claves en correspondencia con los nombre de los atributos de los objetos modelo: # This list contains a Blog object.'}]. cuyo valor por omisión es 'ASC'. 3.5.objects. 'day') [datetime. Cada uno de esos diccionarios representa un objeto.datetime(2005. 1)] >>> Entry.dates('pub_date'.objects.objects. FILTRANDO OBJETOS 281 values(*campos) Retorna un QuerySet especial que evalúa a una lista de diccionarios en lugar de objetos instancia de modelo.datetime(2005. 1)] >>> Entry. 'name': 'Beatles Blog'. 3. 20)] revisión 789 del 9 de noviembre de 2009 .values() [{'id': 1.datetime(2005.'}] a los cuales debe limitarse el values() puede recibir argumentos posicionales opcionales.dates('pub_date'. >>> Blog.

>>> e = Entry...ForeignKey(Person) entonces una llamada a y la City Book..get(id=4) colocará en el cache la Person relacionada relacionada: >>> b = Book.get(id=4) >>> p = b. así que deberías evitarlas de ser posible.objects. el lenguaje de consulta de Django no puede expresar facilmente cláusulas extremos. REFERENCIA DE LA API DE BASE DE DATOS select_related() Retorna un QuerySet que seguirá automáticamente relaciones de clave foránea. extra() A veces.select_related().objects.una forma de inyectar cláusulas especicas Por denición.get(id=5) # Hits the database again to get the related Blog object.blog select_related() sigue claves foráneas tan lejos como le sea posible.hometown # Hits the database.author # Hits the database. Si tienes los siguientes modelos: class City(models. >>> c = p.hometown # Doesn't hit the database. >>> p = b.author # Doesn't hit the database. Esto contribuye a la mejora de rendimiento que resulta en consultas (aveces mucho) más grandes pero signican que el uso posterior de relaciones de clave foránea no requerirán consultas a la base de datos. Los siguientes ejemplos ilustran la diferencia entre búsquedas normales y búsquedas una búsqueda normal: select_related(). el usar damente anidadas. author = models. >>> b = Book. >>> c = p. en siuaciones con conjuntos de relaciones profungenerar consultas tan grandes que terminan siendo lentas. Django provee un modicador de dentro del SQL generado por un QuerySet.Model): # .get(id=4) # No select_related() in this example. >>> b = e. >>> b = e. select_related() puede mejorar muchísimo el desempeño porque tu aplicación puede puede select_related() puede en algunos casos terminar siguiendo demasiadas relaciones y puede entonces evitar muchas llamadas a la base de datos. because e.Model): # . Sin embargo.blog Esta es una búsqueda select_related: # Hits the database. Para estos casos -. seleccionando esos datos adicionales de objetos relacionados cuando ejecuta su consulta.. QuerySet llamado extra() WHERE complejas.objects. select_related no sigue claves foráneas que tienen null=True.objects.objects. >>> e = Entry. hometown = models. Notar que Usualmente. Esta es # Hits the database.blog has been prepopulated # in the previous query..select_related().. revisión 789 del 9 de noviembre de 2009 .Model): # .get(id=5) # Doesn't hit the database.ForeignKey(City) class Book(models.select_related(). estas consultas especiales pueden no ser portables entre los distintos motores de bases de datos (debido a que estás escribiendo código SQL explícito) y violan el principio DRY.282 APÉNDICE C. class Person(models.

Metodos de QuerySet que no devuelven un QuerySet QuerySet y devuelven algo que no es un QuerySet Los métodos de QuerySet que se describen a continuación evaluan el -.C. print "Either the entry or blog doesn't exist. realiza una subconsulta para darle a cada objeto un entero que indica la cantidad de objetos Entry Blog resultante un atributo asociados al blog: >>> subq = 'SELECT COUNT(*) FROM blog_entry WHERE blog_entry. FILTRANDO OBJETOS 283 Se puede especicar uno o más de pero deberías indicar al menos uno. is_recent.extra(select={'entry_count': subq}) (En este caso en particular. o algo así. 20)']) Python: El select y where antes descriptos pueden utilizar los comodines normales para bases de datos en ' %s' para indicar parámetros que deberían ser escapados automáticamente por el motor de la base de datos.get(id=3) . Debe contener un diccionario que mapee nombres de atributo a cláusulas SQL que se utilizarán para calcular el atributo en cuestión: >>> Entry.objects. por ejemplo: >>> Entry.4. entry_count. select permite indicar campos adicionales en una cláusula de SELECT.objects.DoesNotExist La excepción de múltiples excepciones DoesNotExist hereda de django.extra(where=['headline= %s'].exceptions. get(**lookup) Devuelve el objeto que matchee el parámetro de búsqueda provisto.objects. El argumento params.. Todos los argumentos de where son unidos con AND a cualquier otro criterio de búsqueda: >>> Entry. argumento params es una lista de los parámetros que serán utilizados para realizar la sustitución: Los parámetros >>> Entry.extra(where=['headline= %s'].quizás para realizar joins implícitos -.) blog_blog en where. El parámetro debe proveerse de la manera descripta en la sección  Patrones de búsqueda. e = Entry. o tables.objects. b = Blog. También es posible denir cláusulas Tanto Se puede agregar tablas manualmente a la cláusula where como tables reciben WHERE explícitas -. El siguiente ejemplo es más avanzado.core. así que puedes protegerte DoesNotExist: >>> from django. un booleano que repre- del entry es mayor que el 1 de Enero de 2006.objects...id' >>> Blog. 5. un valor.un objeto.. except ObjectDoesNotExist: .. Este es un ejemplo de lo que está incorrecto: Entry.extra(where=["headline=' %s'" % name]) Este es un ejemplo de lo que es correcto: Entry. cada objeto sentará si el atributo pub_date Entry tendrá en este caso un atributo adicional." revisión 789 del 9 de noviembre de 2009 .5. Este método levanta con el patrón provisto.ObjectDoesNotExist. select.exceptions import ObjectDoesNotExist >>> try: .get(id='foo') # levanta Entry.objects. AssertionError si más de un objecto concuerda get() levanta una excepción de DoesNotExist.extra(select={'is_recent': "pub_date > '2006-01-01'"}) Como resultado.. params=[name]) C. where.get(id=1) .core.objects.. Si no se encuentra ningún objeto que coincida con el patrón de búsqueda provisto Esta excepción es un atributo de la clase del modelo. 4. estamos aprovechando el hecho de que la consulta ya contiene la tabla su cláusula FROM. una lista de cadenas.objects.usando el argumento FROM del SQL usando el argumento tables.extra(where=['id IN (3.objects.5. Ninguno de los argumentos es obligatorio.blog_id = blog_blog. params=['Lennon']) Siempre se debe utilizar params en vez de utilizar valores directamente en select o where ya que params asegura que los valores serán escapados correctamente de acuerdo con tu motor de base de datos particular..

get_or_create es utilizado más que nada en scripts que necesiten procePOST salvo que GET no deberían afectar los datos de ningu- sar datos y crear nuevos campos si los que existen no están disponibles. Los pedidos na manera. defaults={'defaults': 'baz'} ) Nota Como ya se mencionó.create(first_name="Bruce".284 APÉNDICE C. Si se encuentra un objecto.pop('defaults'. por favor asegurate de utilizarlo solo en pedidos tengas una buena razón para no hacerlo. birthday=date(1940. last_name="Springsteen") >>> p. v) for k. Está pensado como un atajo para el caso de uso típico y es más que nada útil para scripts de importación de datos. last_name='Lennon') except Person.objects.model(**params) obj. El nuevo objeto será creado de acuerdo con el siguiente algoritmo: defaults = kwargs. 9)} get_or_create() -. 'defaults__exact' así: Foo. 10.save() en una sola línea: >>> p = Person.get_or_create( = 'John'. y que no contengan doble guión bajo (lo cual sobreescribiendo cualquier valor que ya estuviera asignado. Si necesitas utilizar get_or_create() en una vista. created). last_name='Lennon'. = 'Lennon'. revisión 789 del 9 de noviembre de 2009 . created = first_name last_name defaults ) en una llamada a nuevo objeto y Person. = {'birthday': date(1940. 10. Devuelve una tupla es el objecto encontrado o creado. last_name="Springsteen") get_or_create(**kwargs) object Este método sirve para buscar un objeto y crearlo si no existe.objects. por ejemplo: try: obj = Person. Luego se le agrega el contenido de Si el modelo tiene un campo llamado simplemente hay que utilizar defaults. {}) params = dict([(k. v in kwargs.será utilizado get_or_create devolverá una tupla con ese objeto y False.get(first_name='John'. REFERENCIA DE LA API DE BASE DE DATOS create(**kwargs) Este método sirve para crear un objeto y guardarlo en un mismo paso. no se encuentra un objeto. devolviendo una tupla con el Cualquier argumento que se le pase a get().get_or_create( defaults__exact = 'bar'. Si get_or_create() instanciará y guardará un objeto nuevo. 9)) obj.save() Esto es.excepto el argumento opcional defaults -. y se usa el resultado como claves para el constructor del modelo.objects.objects. True.DoesNotExist: obj = Person(first_name='John'. se debe utilizar POST en cualquier pedido a una página que pueda tener como efecto secundario una modicación a tus datos. se comienza con los argumentos que no sean 'defaults' indicaría una búsqueda no exacta). y created (object. El ejemplo anterior puede ser escrito usando obj. Te permite abreviar dos pasos comunes: >>> p = Person(first_name="Bruce".items() if '__' not in k]) params.save() get_or_create así: Este patrón se vuelve inmanejable a medida que aumenta el número de campos en el modelo. donde es un booleano que indica si el objeto fue creado.update(defaults) obj = self. defaults y es necesario usarlo para una búsqueda exacta en get_or_create().

utilizando el campo que se provea en el argumento Meta de tu modelo especica campo indicado en Al igual que get_latest_by. toman la forma de campo__tipodebusqueda=valor (notar el doble guión Los patrones de búsqueda son la manera en que se especica la carne de una cláusula argumentos de palabra clave para los métodos Los parámetros básicos de búsqueda bajo). Este ejemplo devuelve el Entry más reciente en la tabla. Si se suministra un argumento de palabra clave inválido.filter(pub_date__lte='2006-01-01') se traduce (aproximadamente) al siguiente comando SQL: SELECT * FROM blog_entry WHERE pub_date <= '2006-01-01'. se puede omitir el argumento field_name. obtendrás un diccionario vacío. get().C. PATRONES DE BÚSQUEDA 285 count() Devuelve un entero representando el número de objetos en la base de datos que coincidan con el nunca levanta excepciones.g.filter(headline__contains='Lennon'). latest() levanta DoesNotExist si no existe un objeto con los parámetros provistos. count() podría devolver un entero largo en vez de un entero normal de Python.6. ordenados por fecha.objects. la función levantará una excepción de A continuación se listan los tipos de búsqueda que existen. exclude() y get() de QuerySet. el C. >>> Entry.objects. 2: Cheddar Talk} Blog. de acuerdo con el campo pub_date: >>> Entry.. por ejemplo: >>> {1: >>> {1: >>> {} Blog. He aquí un ejemplo: QuerySet. Patrones de búsqueda WHERE de SQL.in_bulk([]) Si no se encuentra un objeto en la base para un ID en particular. count() # Returns the total number of entries in the database. 2]) Beatles Blog. Esto es una característica particular de la implementación subyacente que no debería ser ningún problema en la vida real.objects.objects. este id no aparecerá en el diccionario resultante. revisión 789 del 9 de noviembre de 2009 .in_bulk([1.count() 1 los registros en objetos Python y luego invocar count() en el fondo realiza un SELECT COUNT(*).objects. Django utilizará get_latest_by por defecto.6. Consisten de filter(). PostgreSQL o MySQL). así que deberías siempre utilizar count() en vez de cargar todos len() sobre el resultado. latest(eld_name=None) field_name como fecha.objects.count() 4 # Returns the number of entries whose headline contains 'Lennon' >>> Entry. Si le pasas una lista vacía a in_bulk().objects. in_bulk(id_list) Este método toma una lista de claves primarias y devuelve un diccionario que mapea cada clave primaria en una instancia con el ID dado.latest('pub_date') Si el Devuelve el último objeto de la tabla. Dependiendo de la base de datos que estés utilizando (e. Por ejemplo: >>> Entry. TypeError.in_bulk([1]) Beatles Blog} Blog.

el símbolo de porciento indica una secuencia de caracteres cualesquiera.objects. contains Realiza una búsqueda de subcadenas.6. LIKE distinguiendo mayúsculas y minúsculas. y el guión bajo y Esto signica que las cosas deberían funcionar de manera intuitiva. Tanto el símbolo de porcentaje como el guión bajo se deberían manejar de manera transparente. Por ejemplo.2. por que la abstracción funciona bien. distinguiendo mayúsculas y minúsculas: Entry. Por ejemplo.objects.get(headline__exact="Man bites dog") Esto busca objetos que tengan en el campo headline la frase exacta Man bites dog. LIKE (iexact. revisión 789 del 9 de noviembre de 2009 .4. iendswith) escaparán automáticamente los dos caracteres especiales utilizados en sentencias LIKE -.objects.6. 'Today Lennon honored' pero no con 'today lennon honored'. C. WHERE headline LIKE ' %\ % %'. El SQL resultante será algo similar a esto: SELECT .el tipo de búsqueda se asume como exact. C.. (En una sentencia LIKE.3. icontains Realiza una búsqueda de subcadenas. 'BeAtLes BLoG'. simplemente hace falta utilizar el símbolo de porcentaje como cualquier otro caracter: Entry.get(headline__icontains='Lennon') A diferencia de contains. contains.objects.O sea.. si tu argumento de palabra clave no contiene un doble guión bajo -. istartswith. las siguientes dos sentencias son equivalentes: >>> Blog.get(id=14) # __exact is implied Esto es por conveniencia. etcétera.objects. C.objects. exact Realiza una búsqueda por coincidencias exactas: >>> Entry. iexact Realiza una búsqueda por coincidencias exactas sin distinguir mayúsculas de minúsculas: >>> Blog.filter(headline__contains=' %') Django se hace cargo del escapado.get(headline__contains='Lennon') Esto coincidirá con el titular SQLite no admite sentencias comporta como icontains.el porciento y el guión bajo.286 APÉNDICE C. Si no se suministra un tipo de búsqueda -.objects. Lo mismo vale para el guión bajo. REFERENCIA DE LA API DE BASE DE DATOS C.get(name__iexact='beatles blog') Traerá objetos con nombre 'Beatles Blog'. 'beatles blog'. contains se Escapado de porciento y guión bajo en sentencias Los patrones de búsqueda que resulten en sentencias SQL LIKE startswith. cuando se utiliza SQLite.6. para obtener todos los Entries que contengan un símbolo de porciento.1.get(id__exact=14) # Explicit form >>> Blog. icontains sí trerá today lennon honored. dado que las búsquedas con tipo de búsqueda exact son las más frecuentes. icontains.6. endswith. indica un solo caracter cualquiera). sin distinguir mayúsculas y minúsculas: >>> Entry.

la cadena 4 resulta ser mayor que la cadena 10).filter(pub_date__range=(start_date. range Realiza una búsqueda por rango: >>> start_date = datetime.6. 3.6.filter(headline__iendswith='cats') C. y will found in crypt.6. startswith Busca coincidencias de prejos distinguiendo mayúsculas y minúsculas: >>> Entry.date(2005. C. distinguiendo y sin distinguir mayúsculas de minúsculas.e. sin distinguir mayúsculas y minúsculas: >>> Entry. pero no Who is Will? o will found in crypt. números. istartswith Realiza una búsqueda por prejos.date(2005.filter(id__gte=1) Estas consultas devuelven cualquier objeto con un ID mayor a 4. pero no Who is Will? C. gt. end_date)) Se puede utilizar range en cualquier lugar donde podrías utilizar BETWEEN en SQL -..objects. Por lo general estos operadores se utilizarán con campos numéricos. 1.objects. PATRONES DE BÚSQUEDA 287 C. 1) >>> end_date = datetime. gte.5. respectivamente.9. in Aplica un ltro para encontrar valores en una lista dada: Entry. Willbur named judge.objects.6.para fechas. 3.filter(headline__startswith='Will') Esto encontrará los titulares Will he run? y Willbur named judge.6.10. e incluso cadenas de caracteres. ya que el orden no siempre es el que uno se esperaría (i. 3 o 4.objects. revisión 789 del 9 de noviembre de 2009 .objects.7.filter(headline__endswith='cats') >>> Entry.filter(id__in=[1. 4]) Esto devolverá todos los objetos que tengan un ID de 1.6. endswith and iendswith Realiza búsqueda de sujos. respectivamente: >>> Entry.8. menor a.objects.objects.filter(headline__istartswith='will') Esto devolverá los titulares Will he run?. y un ID mayor o igual a 1.filter(id__lt=15) >>> Entry. 31) >>> Entry.6.6. C.objects. mayor o igual a. Se debe tener cuidado con los campos de caracteres. lt. un ID menor a 15. respectivamente: >>> Entry.objects.filter(id__gt=4) >>> Entry.C. and lte Estos representan los operadores de mayor a. C. y menor o igual a.

objects.filter(blog__pk=3) # __pk implica __id__exact revisión 789 del 9 de noviembre de 2009 .objects.11. estas tres sentencias son equivalentes: >>> Entry.filter(pub_date__isnull=True) __isnull=True a vs. que aprovecha el indexado pero signicativamente más rápido debido al indexado datos para agregar el índice full-text.6.4.objects.13.filter(pub_date__month=12) # Búsqueda por día >>> Entry.6.toma enteros >>> Entry. ya que SQL requiere que ningún valor sea igual NULL.objects.filter(pub_date__year=2005) # Búsqueda por mes -. C. mes o día: # Búsqueda por año >>>Entry.6.filter(blog__id__exact=3) # Forma explícita >>> Entry. así que estas sentencias serían equivalentes: >>> Blog. la clave pk. pub_date__day=25) C.objects.14.filter(pk__gt=14) Las búsquedas pk también funcionan con joins. __exact=None __isnull=True y Hay una diferencia importante entre __exact=None.get(id=14) # __exact implícito >>> Blog.6. que corresponderán a consultas SQL de IS NULL``y ``IS NOT NULL. respectivamente: >>> Entry. 4.filter(pub_date__day=3) # Combinación: devuelve todas las entradas de Navidad de cualquier año >>> Entry. o 7 >>> Blog. Por ejemplo. __exact=None NULL siempre devolverá como resultado un conjunto vacío.objects.filter(pub_date__month=12. realiza búsqueda exacta por año.objects. month.objects.filter(pk__in=[1.7]) # Buscar entradas en blogs con id > 14 >>> Blog.objects. and day Para campos date y datetime. isnull Toma valores True o False.filter(blog__id=3) # __exact implícito >>> Entry.objects. que realiza una búsqueda sobre la clave primaria del primaria es el campo id.objects.cualquier patrón de búsqueda puede ser combinado con pk para realizar una búsqueda sobre la clave primaria de un modelo: # Buscar entradas en blogs con id 1. Django provee un patrón de búsqueda primary key.288 APÉNDICE C. __isnull determina si el campo actualmente contiene un valor sin realizar la comparación.get(pk=14) # pk implica id__exact El uso de pk no se limita a búsquedas __exact -. C. Esto es como contains Nótese que este tipo de búsqueda sólo está disponible en MySQL y requiere de manipulación directa de la base de full-text. REFERENCIA DE LA API DE BASE DE DATOS C.objects.objects. year.12. modelo de ejemplo Blog. El patrón de búsqueda pk modelo (pk por En el Por conveniencia. full-text.get(id__exact=14) # Forma explícita >>> Blog. search Un booleano que realiza búsquedas full-text. del inglés).

C.entry_set. recibir también uno o más objetos Q con los operadores & y |. 5. Búsquedas complejas con Objetos Q filter() por ejemplo -.g. Todos Poll. e es un objeto Entry. LIKE: & Los argumentos de palabras clave en las búsquedas -. puede acceder a su Blog asociado accediendo al atributo blog.C. las instan- Cuando denes una relación en un modelo (i. equivalente al ejemplo anterior.get( Q(pub_date=date(2005. 5. get()) como argumento posicional (no nombrado). si través del atributo de esta sección. si se provee un objeto ejemplo. 6)). 5. 2)) | Q(pub_date=date(2005.7. Estos argumentos de palabra clave son especicados como se indica en la sección  Patrones de búsqueda. 6))) no es válido.el vínculo del modelo relacionado al modelo que dene la relación.djangoproject.e. 5. objeto Django también crea una API para acceder al otro lado de la relación -.com/documentation/0. por ejemplo: Poll. Por ejemplo. este objeto encapsula una consulta con un único Q(question__startswith='What') Los objetos Q pueden ser combinados utilizando los operadores objetos. b es un entry_set: b.g. tiene acceso a la lista de todos los objetos Entry a Todos los ejemplos en esta sección utilizan los modelos de ejemplo Blog. los argumentos serán unidos con AND.en Un objeto realizar búsquedas más complejas (e.objects. Hay algunos ejemplos disponibles online en http://www.blog. Author y Entry que se denen al principio . lo siguiente: los argumentos provistos a una función de búsqueda (sean argumentos de palabra clave u objetos Q) son unidos con Q debe preceder la denición de todos los argumentos de palabra clave.objects. de dos consultas question__startswith Cuando se utiliza un operador sobre dos sería: Q(question__startswith='Who') | Q(question__startswith='What') Esto será equivalente a la siguiente cláusula WHERE en SQL: WHERE question LIKE 'Who %' OR question LIKE 'What %' Puede componer sentencias de complejidad arbitraria combinando objetos pueden utilizar paréntesis para agrupar. Q(pub_date=date(2005.db. pero esto: # CONSULTA INVALIDA Poll.7. Por Q y de argumentos de palabra clave.models.. Por ejemplo. un OR y |. se obtiene un nuevo objeto Q. un Por ejemplo. 6)) ) se traduce aproximadamente al siguiente SQL: SELECT * from polls WHERE question LIKE 'Who %' AND (pub_date = '2005-05-02' OR pub_date = '2005-05-06') Las funciones de búsqueda pueden además mezclar el uso de objetos AND. 2)) | Q(pub_date=date(2005.son unidos con AND. Si se proveen multiples objetos Q como argumentos a una función de búsqueda. BÚSQUEDAS COMPLEJAS CON OBJETOS Q 289 C. Por ejemplo. Q(pub_date=date(2005.get( question__startswith='Who'. Sin embargo. Si necesitas OR). exclude(). revisión 789 del 9 de noviembre de 2009 Blog. Objetos Relacionados ForeignKey. 5.8. si cias de ese modelo tendrán una API conveniente para acceder a estos objetos relacionados.all(). puedes utilizar objetos Q. Cualquier función de búsqueda que tome argumentos de palabra clave (e. question__startswith='Who') es una consulta válida. búsquedas con sentencias Q (django.96/models/or_lookups/ .. 5. También se puede Q filter(). OneToOneField.Q) Q es un objeto que se utiliza para encapsular una colección de argumentos de palabra clave. or ManyToManyField).get( Q(question__startswith='Who').objects. 2)) | Q(pub_date=date(2005. esto es e.

save() Si un campo ForeignKey tiene la opción null=True seteada (i.8. Aquí se muestra un ejemplo: ForeignKey que apunte a otro modelo. Este ejemplo busca todos los objetos JOINs de SQL de manera automática. un campo está documentada en la sección  Métodos de consulta que retornan nuevos QuerySets.save() # "UPDATE blog_entry SET blog_id = NULL .290 APÉNDICE C. por ejemplo: e = Entry.blog # No va a la base de datos.filter(blog__name__exact='Beatles Blog') Este camino puede ser tan largo como quieras. simplemente hay que utilizar el nombre en minúsculas del modelo.2. las instancias de ese modelo tendrán acceso al objeto relacionado (foráneo) vía un simple atributo del modelo.objects. También Funciona en la otra dirección. Relaciones de Clave Foreánea Inversas Las relaciones de clave foránea son automáticamente simétricas -. se le puede asignar None: e = Entry.blog # Devuelve el objeto Blog relacionado Se puede acceder y asignar el valor de la clave foránea vía el atributo. print e.blog # No va a la base de datos.8. print e. por ejemplo: e = Entry. Notar que el método de a-muchos de la instancia: QuerySet select_related() busca inmediatamente todos los objetos de relaciones uno- e = Entry. Este Manager devuelve QuerySets.get(id=2) print e. los cambios a la clave foránea no se guardan en el modelo hasta que invoques el método save().blog = None e. hasta que llegues al campo que Entry que tengan un Blog cuyo nombre sea 'Beatles Blog': >>> Entry.objects. permite valores NULL).blog = some_blog e. que pueden ser ltradas Si un modelo tiene una devuelve todas las instancias del primer modelo.objects.objects. Por defecto. usa la versión cacheada..objects. Este ejemplo busca todos los objetos Blog que tengan al menos un Entry cuyo headline contenga 'Lennon': >>> Blog. Relaciones de Clave Foránea Si un modelo contiene un ForeignKey.get(id=2) e.8. usa la versión cacheada.objects. Como sería de esperar. haciéndose cargo de los necesitabas.3.e." El acceso a relaciones uno-a-muchos se almacena la primera vez que se accede al objeto relacionado.get(id=2) print e.. este y manipuladas como se describe en la sección  Recuperando objetos. donde FOO es el nombre modelo que contiene la clave foránea. Consultas Que Cruzan Relaciones Django ofrece un mecanismo poderoso e intuitivo para seguir relaciones cuando se realizan búsquedas. las instancias del modelo de la clave foránea tendrán acceso a un Manager que Manager se llama FOO_set.blog # Busca el Blog asociado en la base de datos.get(id=2) e.select_related(). Para cruzar una relación simplemente hace falta utilizar el nombre de campo de los campos relacionados entre modelos.. Para referirse a una relación inversa.blog # No va a la base de datos.get(id=2) e. todo en minúsculas. usa la versión cacheada.objects. select_related() C. separados por dos guiones bajos. por ejemplo: e = Entry. revisión 789 del 9 de noviembre de 2009 . Cualquier acceso subsiguiente a la clave foránea del mismo objeto son cacheadas.se inere una relación inversa de la presencia de ForeignKey.filter(entry__headline__contains='Lennon') C.1. REFERENCIA DE LA API DE BASE DE DATOS C.

b.simplemente los desasocia..objects.date(2005. el ejemplo anterior pasaría a ser así: b = Blog. obj2.ya ha sido guardado Esto es equivalente a (pero más simple que) lo siguiente: b = Blog.entry_set # Raises AttributeError: "Manager must be accessed via instance".create(headline='Hello'.get(id=1) b. entonces un objeto no puede ser quitado de una relación sin ser agregado a otra.objects.): Quita los objetos indicados del conjunto de objetos relacionados: b = Blog.clear() Notar que esto no borra los objetos relacionados -.entry_set() es equivalente a hacer e.blog = None.get(id=234) b.get(id=234) b. Por ejemplo.entry_set. OBJETOS RELACIONADOS 291 b = Blog. 1)) e.entry_set. pub_date=datetime.entry_set.filter(headline__contains='Lennon') b.filter(headline__contains='Lennon') b. Además de los metodos de QuerySet denidos en la sección  Recuperando Objetos. body_text='Hi'. headline='Hello'.count() Se puede cambiar el nombre del atributo FOO_set indicando el parámetro related_name en la denición del ForeignKey(). esto es una acción inválida.entries. Quita todos los objetos del conjunto de objetos relacionados: clear(): b = Blog. blog a create().objects.save() Notar que no es necesario especicar el argumento de palabra clave correspondiente al modelo que dene la relación. 1.all() # Encontrar todos los objetos Entry relacionados a b. si el modelo Entry fuera cambiado por blog = ForeignKey(Blog.objects. el quitar a e de b.. debe ser accedido desde una instancia: Blog.get(id=1) e = b.entry_set.get(id=1) b.objects.objects.save() acá -.): por ejemplo: Agrega los objetos del modelo indicado al conjunto de objetos relacionados. pub_date=datetime.date(2005.. b. Django deduce remove(obj1.get(id=1) e = Entry(blog=b.entry_set es un Manager que devuelve QuerySets.objects. # b. b = Blog. Al igual que remove().. En el ejemplo anterior. body_text='Hi'.entries. .entries es un Manager que devuelve QuerySets.objects. Para evitar inconsistencias en la base de datos. este método sólo existe para objetos ForeignKey donde null=True. lo guarda. clear solo está disponible para campos ForeignKey donde null=True.objects. related_name='entries'). y lo deja en el conjunto de objetos relacionados. create(**kwargs): Crea un nuevo objeto.entry_set.count() No se puede acceder al Manager de ForeignKey inverso desde la clase misma. 1. 1) # No hace falta llamar a e. obj2. En el ejemplo anterior.entries.get(id=1) e = Entry.all() # Encontrar todos los objetos Entry relacionados a b.remove(e) # Desasociar al Entry e del Blog b. . # b. Devuelve el objeto recién creado: b = Blog. no le pasamos el parámetro que el campo blog del nuevo Entry debería ser b.get(id=1) b.8.entry_set. Si el campo relacionado no puede pasar ser None (NULL).C.get(id=1) e = Entry.entry_set. revisión 789 del 9 de noviembre de 2009 . el Manager de ForeignKey tiene los siguientes métodos adicionales: add(obj1. y dado que la denición del campo ForeignKey blog (en el modelo Entry) no indica null=True.add(e) # Associates Entry e with Blog b.

por ejemplo: b = Blog. Por ejemplo.objects. En el ejemplo ManyToManyField en el modelo Entry indicara related_name='entries'. Los desarrolladores Django creen que esto es una violación del principio DRY (Don't Repeat Yourself ).id) # Query using id from instance Entry.filter(blog=b. Pero cómo es esto posible.8. Este método inmediatamente borra el objeto y no tiene ningún valor El métodos para borrar se llama de retorno: revisión 789 del 9 de noviembre de 2009 .9.objects.get(id=5) a.all() # Devuelve todos los objetos Author para este Entry. todos los objetos pre-existentes serán quitados del todos los objetos en el iterable (en este caso.entry_set = [e1. Un ejemplo de esto lo hará más fácil de entender: e = Entry. una de las funciones de INSTALLES_APPS es indicarle a Django el dominio completo de modelos que se utiliza.authors. borradura y agregado son inmediata y automáticamente grabados en la base de datos.objects. Esencialmente. mientras que el modelo inverso utiliza el nombre del modelo original. La única diferencia es el nombrado de los atributos: el modelo que dene el campo con el sujo ManyToManyField usa el nombre del atributo del campo mismo. e2] Si el método clear() está denido. Relaciones muchos-a-muchos Ambos extremos de las relaciones muchos-a-muchos obtienen una API de acceso automáticamente. Consultas que Abarcan Objetos Relacionados Las consultas que involucran objetos relacionados siguen las mismas reglas que las consultas que involucran campos normales. así que Django sólo te exige que denas la relación en uno de los extremos. si b es un objeto Blog con id=5. se puede utilizar tanto una instancia del modelo o bien el valor de la clave primaria del objeto.get(id=1) b. C. Cómo son posibles las relaciones inversas? El mapeador objeto-relacional requiere que denas relaciones en ambos extremos. los ManyToManyField pueden indicar un related_name.292 APÉNDICE C.entry_set. Borrando Objectos delete(). REFERENCIA DE LA API DE BASE DE DATOS Para asignar todos los miembros de un conjunto relacionado en un solo paso. las tres siguientes consultas son idénticas: Entry. Si el método entry_set antes de que clear() no está disponible.objects. '_set' (tal como lo hacen las relaciones uno-a-muchos).count() e. Al igual que los campos anterior. La API funciona igual que las funciones inversas de las relaciones uno-a-muchos (descriptas en la sección anterior). e. en minúsculas.8.filter(blog=5) # Query using id directly C. todos los objetos del iterable son agregados al conjunto sin quitar antes los objetos pre-existentes. si el campo Author tendría un ForeignKey.all() # Devuelve todos los obejtos Entry para este Author.4.filter(name__contains='John') a = Author. INSTALLED_APPS y crea las relaciones inversas en memoria como sea necesario. simplemente se le asigna al conjunto un objeto iterable.5.objects. La primera vez que se carga cualquier modelo. cualquier instancia de atributo entries en vez de entry_set.objects. dado que una clase modelo no sabe qué otros modelos se relacionan con él hasta que los otros modelos sean cargados? La respuesta yace en la variable Django itera sobre todos los modelos en INSTALLED_APPS. Todas las operaciones inversas denidas en esta sección tienen efectos inmediatos en la base de datos. Cuando se indica el valor que se requiere en una búsqueda.authors.authors. C. Toda creación.get(id=3) e. la lista) sean agregados al conjunto.filter(blog=b) # Query using object instance Entry.

objects.96/models/lookup/.gender 'M' >>> p. 'Female').djangoproject. Este método devuelve el valor humanamente legible del campo. Estos métodos devuelven el objeto siguiente y anterior en orden cronológico respecto del campo en cuestión.10. ('F'. emula el comportamiento de la restricción de SQL también.en otras palabras. todos los objetos que tengan una clave foránea que apunte al objeto que está siendo borrado serán borrados b = Blog. que deberían ser de la forma descripta en la sección  Patrones de búsqueda.10.save() >>> p.delete() C. donde FOO es el nombre del campo. por ejemplo: ON DELETE CASCADE -. gender='M') >>> p. Esto es un Entry. esto borra todos los objetos Entry tiene un método delete() que borra todos los que tengan un año de pub_date igual a Entry. Por ejemplo. el objeto tendrá dos métodos get_next_by_FOO() get_previous_by_FOO().10.all().delete() Cuando Django borra un objeto. choices=GENDER_CHOICES) cada instancia de Person tendrá un método get_gender_display: >>> p = Person(name='John'.Model): name = models. MÉTODOS DE INSTANCIA ADICIONALES 293 e.10. revisión 789 del 9 de noviembre de 2009 .objects. Si realmente quieres borrar todos los objetos.delete() Notar que delete() es el único método de QuerySet que no está expuesto en el mecanismo de seguridad para evitar que accidentalmente solicites Manager mismo. C. 'Male'). Métodos de Instancia Adicionales save() y Además de delete().1.CharField(max_length=1. un objeto modelo puede tener cualquiera o todos de los siguientes métodos. b.objects. Ambos métodos aceptan argumentos de palabra clave opcionales. ) class Person(models.get(pk=1) # Esto borra el Blog y todos sus objetos Entry.delete() y borres todos los Entry.2.CharField(max_length=20) gender = models.delete() También se puede borrar objetos en grupo.objects.com/documentation/0.C. y get_next_by_FOO(**kwargs) y get_previous_by_FOO(**kwargs) Por cada campo DateField y DateTimeField que no tenga null=True. get_FOO_display() Por cada campo que indica la opción modelo: choices. Todo objeto miembros de ese 2005: QuerySet. en http://www. hay que pedirlo explícitamente al conjunto completo de objetos: Entry. QuerySet Por ejemplo.get_gender_display() 'Male' C.filter(pub_date__year=2005). levantando la excepción DoesNotExist cuando no exista tal objeto. estos métodos utilizarán el ID como un chequeo secundario. Notar que en el caso de valores de fecha idénticos. el objeto tendrá un método get_FOO_display(). Esto garantiza que no se saltearán registros ni aparecerán duplicados. Hay un ejemplo completo en los ejemplos de la API de búsqueda. respectivamente. en el siguiente GENDER_CHOICES = ( ('M'. donde FOO es el nombre del campo.

name='Fred') # Use a custom manager 'recent_entries' in the search for an # entry with a primary key of 3 e = get_object_or_404(Entry. Este os. donde FOO es el nombre del campo. y una cantidad arbitraria de argumentos de palabra clave. así que todo modelo que tenga un obtendrá también este método. (La implementación de este método utiliza FileField el objeto tendrá un método get_FOO_size(). se utiliza el se le puede pasar a Manager en vez: por defecto para ejecutar la consulta get() subyacente. Este método guarda el archivo en el sistema de archivos. get_object_or_404() get() y levantar un Http404 si el objeto no existe. pk=3) Cuando se le pasa un modelo a esta función.) C. get_object_or_404() un objeto Manager # Get the author of blog instance e with a name of 'Fred' a = get_object_or_404(e. en pixeles. o si quiere buscar en una lista de objetos relacionados.10.11. raw_contents) Por cada campo FileField. el objeto tendrá un método save_FOO_file(). Si un archivo con el nombre dado ya existe.recent_entries. el método C. Django le agrega guiones bajos al nal del nombre de archivo (pero antes de la extensión) hasta que el nombre de archivos esté disponible. get_FOO_lename() Todo campo FileField le dará al objeto un método get_FOO_filename(). donde FOO es el nombre del campo. get_FOO_url(). Esta funcion toma un modelo Django como su primer argumento. descubrirás una serie de modismos en la manera de utilizar la API de la base de datos. Este MEDIA_URL.10. C. Si esta variable está vacía.10. como C. Atajos (Shortcuts) A medida que desarrolles tus vistas. get_FOO_size() Por cada campo método devuelve el tamaño del archivo. donde FOO es el nombre del campo.10.path. Luego levanta un Http404 si el objeto no existe. el objeto obtendrá dos métodos. get_FOO_url() Por todo campo FileField el objeto tendrá un método método devuelve la URL al archivo. que le pasa al método get() del Manager por defecto del modelo. get_FOO_height() and get_FOO_width() Por cada campo un entero. Django codica algunos de estos modismos como atajos que pueden ser utilizados par simplicar el proceso de escribir vistas.5.getsize.3. ImageField.authors.6. REFERENCIA DE LA API DE BASE DE DATOS C. por ejemplo: Un modismo frecuente es llamar a en la función # Get the Entry with a primary key of 3 e = get_object_or_404(Entry. C. Estas funciones se pueden hallar en el módulo django. Si no quieres que se utilice el manager por defecto. utilizando el nombre dado. en bytes. save_FOO_le(lename.10. Estos métodos devuelven el alto y el ancho (respectivamente) de la imagen.11. Este modismo es capturado get_object_or_404(). donde FOO es el nombre del campo. get_FOO_height() y get_FOO_width(). donde FOO es el nombre del campo.294 APÉNDICE C. de acuerdo con la variable Notar que el campo campo ImageField ImageField es técnicamente una subclase de FileField. de acuerdo con tu variable devolverá una cadena vacía.shortcuts.7. Esto devuelve el nombre de archivo completo en el sistema de archivos. MEDIA_ROOT. pk=3) revisión 789 del 9 de noviembre de 2009 .4. C.1.

filter() en vez de a C. lo cual es una idea astuta desde el punto de vista de organización del código.. Finalmente.11. todavía puede optar por escribir la sentencia directamente en SQL crudo. Por más instrucciones.12. Puedes acceder a la base de datos utilizando otras herramientas. Aunque no exista ningún requisito en Django que exija que las consultas a la base de datos vivan en la capa del modelo. get_list_or_404() salvo porque llama a get_list_or_404() se comporta igual que get_object_or_404(). Levanta un Http404 si la lista resulta vacía.2.12. lenguajes de programación o frameworks de bases de datos -. esta implementación pone a toda tu lógica de acceso a los datos en un mismo lugar.C. UTILIZANDO SQL CRUDO 295 C. La forma preferida para hacer esto es dándole a tu modelo métodos personalizados o métodos de Manager person- alizados que realicen las consultas. Utilizando SQL Crudo Si te encuentras necesitando escribir una consulta SQL que es demasiado compleja para manejarlo con el mapeador de base de datos de Django. revisión 789 del 9 de noviembre de 2009 . véase el Apéndice B.No hay nada especícamente de Django acerca de tu base de datos. get(). es importante notar que la capa de base de datos de Django es meramente una interfaz a tu base de datos.

296 APÉNDICE C. REFERENCIA DE LA API DE BASE DE DATOS revisión 789 del 9 de noviembre de 2009 .

1. En el `Capítulo 10`_ se explica con detalle la lista de procesadores de contexto adicionales. revisión 789 del 9 de noviembre de 2009 . Este argumento se puede usar si queremos modicar el nombre que se genera automáticamente a partir del QuerySet. del cual se leerán los objetos a uti- lizar por la vista. su comportamiento será tal y como se describe en esta tabla. la vista genérica lo ejecutará justo antes de representar la plantilla El tipo MIME a usar para el documento resultante. En el apéndice C hay más información acerca de QuerySet. el Por defecto es Véase `Capítulo 10`_ donde se da más información acerca de los cargadores de plantillas. El nombre completo de la plantilla a usar para representar la página. que se aplican a la plantilla de la vista.loader. extra_context mimetype queryset Un diccionario cuyos valores se añaden al contexto de la plantilla. junto con las opciones que cada una de ellas puede aceptar. context_processors Es una lista de procesadores de contexto adicionales (además de los incluidos por el sistema). Si vale por defecto es allow_empty False y no hay objetos.all()) los objetos text/html. Muchos de esos argumentos funcionan igual para la mayoría de las vistas. Argumento Descripción Un valor booleano que indica como debe comportarse la vista si no hay objetos disponibles. QuerySet (por la variable de conguración ejemplo.template. cargador plantillas a utilizar. Por defecto DEFAULT_MIME_TYPE. Un objeto de utiliza el tipo denido en cuyo valor inicial es tipo Author. Si se almacena un objeto que sea invocable. La tabla D-1 describe estos argumentos comunes. Tampoco viene mal un repaso a los modelos Publisher y Author Book.1: Argumentos comunes de las vistas genéricas.Apéndice D Referencia de las vistas genéricas El Capítulo 9 es una introducción a las vistas genéricas. pero pasa por alto algunos detalles . ya que serán usados en los ejemplo incluidos en esta apéndice. Este apéndice describe todas las vistas genéricas. D. Cuadro D.objects. de La mayoría de las vistas genéricas necesitan este argumento. denidos en dicho capítulo. Antes de intentar entender este material de referencia es muy conveniente leer el Capítulo 9 . template_loader template_name El django. cada vez que veas uno de estos argumentos en la lista de parámetros admitidos por una vista genérica. Su valor Falsa. la vista elevará un error 404 en vez de mostrar una página vacía. Argumentos comunes a todas las vistas genéricas La mayoría de las vistas aceptan varios argumentos que pueden modicar su comportamiento.

Por defecto.defaults import * from django. ('^foo/(?p<id>\d+)/$'.urls.direct_to_template Esta vista representa una plantilla.298 APÉNDICE D.html'}).html. y Ejemplo Dada la siguiente conguración del URLconf: from django. si los hubiera. las vistas de listados o de archivos por fechas).generic.views. ) Este ejemplo devuelve una respuesta Gone para cualquier petición a /bar/: revisión 789 del 9 de noviembre de 2009 . se '_list' al valor de este parámetro.simple'. así que si no se object_list. D.generic.views. Django retornará un mensaje de error 410 (Gone según el estándar Esta vista redirige a otra URL. El nombre completo de la plantilla a representar.1: Argumentos comunes de las vistas genéricas. Para las listas que utilizan más de objeto (por ejemplo. REFERENCIA DE LAS VISTAS GENÉRICAS Cuadro D.generic. es añade el sujo template_object_name 'object'. La URL que se pasa como parámetro puede tener secuencias de formato aptas para diccionarios. {'template': 'foo_index.2. ) Una petición a con una variable de contexto /foo/ mostraría la plantilla foo_index.id }} cuyo valor sería 15.simple import direct_to_template urlpatterns = patterns(''.simple hay varias vistas sencillas que manejan unos cuantos problemas Dentro del módulo frecuentes: mostrar una plantilla que no necesita una vista lógica.views. a la que se le pasa una variable de plantilla accesible como que es un diccionario que contiene los parámetros capturados de la URL.generic. estos estarán accesibles mediante una variable llamada D.simple import redirect_to urlpatterns = patterns('django. {'url': '/bar/ %(id)s/'}).2.html {{ params.views. {'template': 'foo_detail.conf. redirect_to.redirect_to None.1. y hacer una redirección de una página. {{ params }}.views. que serán interpretadas contra los parámetros capturados desde la URL origen.defaults import * from django.views. Si la URL pasada como parámetro es HTTP).simple. Argumentos obligatorios template: D. Ejemplo Este URLconf redirige desde /foo/<id>/ a /bar/<id>/: from django. indica nada y la vista utiliza varios objetos. Argumento Descripción El nombre de la variable principal en el contexto de la plantilla. Redirigir a otra URL Vista a importar : django. (r'^foo/(?P<id>\d+)/$'.urls. direct_to_template.generic.html'}).simple.generic. Vistas genéricas simples django.2. direct_to_template. y una solicitud a /foo/15/ mostraría foo_detail.conf.2. Representar una plantilla Vista a importar : django. (r'^foo/$'.

views. (r'authors/$'. Vistas de listado/detalle Django.views.simple'. 'allow_empty': True. Ejemplo Si consideramos el objeto Author tal y como se denió en el capítulo 5. los resultados serán paginados. esta vidta acepta cualquiera de los siguientes argumentos opcionales descritos en la tabla D-1: allow_empty context_processors extra_context mimetype revisión 789 del 9 de noviembre de 2009 .generic. {'url': None}). de forma que se distribuirán por varias páginas de resultado. list_detail. } urlpatterns = patterns(''. En la siguiente sección hay una nota sobre paginación donde se explica con un poco más de detalle este sistema.views. ) Argumentos obligatorios url: La URL a la que redirigir. Listas de objetos Vista a importar : django.generic.1. podemos usar la vista object_list para obtener un listado sencillo de todos los autores usando el siguiente URLconf: from mysite.defaults import * from django.urls. author_list_info) ) Argumentos obligatorios queryset: Un QuerySet de los objetos a listar (Véase la table D-1).generic.views.3. el índice comienza en cero.list_detail) se encar- Las vistas genéricas de listados/detalle (que residen en el módulo uno de los elementos (el detalle).views. D. ('^bar/$'.object_list.3.D. La vista determinará que página de resultados debe mostrar o bien desde un parámetro page incluido en la URL (vía Get) o mediante una variable page especicada en el URLconf. redirect_to. o None si quereremos devolver una respuesta 410 (Gone según el estándar HTTP).all(). en forma de cadena de texto.object_list Esta vista sirve para representear una lista de objetos. Según se especique en este parámetro.models import Author from django.objects. Además.generic.3.conf.books.simple import redirect_to urlpatterns = patterns('django. VISTAS DE LISTADO/DETALLE 299 from django.generic import list_detail author_list_info = { 'queryset' : Author. Argumentos opcionales paginate_by: es un número entero que especica cuantos objetos se deben mostrar en cada página. En cualquiera de los dos casos. gan de la habitual tarea de mostrar una lista de elementos por un lado (el listado) y una vista individual para cada D.list_detail.

D. Django paginará los resultados. tendriamos que el queryset sería Author. usando un índice basado en 1. (Su valor es el mismo que el del parámetro paginate_by). Contexto de plantilla Además de los valores que se puedan haber denido en siguientes valores: extra_context.generic.all(). por lo que la etiqueta de la books y el nombre del modelo es author. de la clase del modelo. is_paginated: Un valor booleano que indicará si los resultados serán paginados o no. Por ejemplo.objects. la vista usará una plantilla llamada <app_label>/<model_name>_l queryset. Una nota sobre paginación Si se utiliza el parámetro en cero. has_next: Un valor booleano indicando si hay una siguiente página. paginate_by. y vale 'object' por defecto. no la número 0. Concretamente. siendo 1 la primera página. tu URLconf podría pare- visualizar usando dos métodos diferentes: Usar un parámetro cerse a este: (r'^objects/page(?P<page>[0-9]+)/$'. Si se deniera template_object_name como 'foo'. has_previous: Un valor booleano indicando si hay una página previa. Si los resultados están paginados. el nombre de la plantilla a utilizar por books/author_list.list_detail. dict(info_dict)) Pasar el número de la página mediante un parámetro ejemplo.views. no pages: El número total de páginas. este valor seguirá siendo un numero entero que apuntaría a una hipotética siguiente página. aplicación será defecto será template_name.html.object_detail revisión 789 del 9 de noviembre de 2009 Esta vista proporciona una representación indidual de los detalles de un objeto. en En el ejemplo anterior. 'object_list'. page: El número de la página actual. el nombre de esta variable sería foo_list. Con esos datos. .300 APÉNDICE D. Vista de detalle Vista a importar : django. y la etiqueta de modelo es el nombre. tus URL se podrían parecer a esto: page en la URL: Por /objects/?page=3 En ambos casos. next: El número de la siguiente página. REFERENCIA DE LAS VISTAS GENÉRICAS template_loader template_name template_object_name Nombre de la plantilla Si no se ha especicado el parámetro opcional Tanto la etiqueta de la aplicación como la etiqueta del modelo se obtienen del parámetro minúsculas. previous: El número de la anterior página. el contexto dispondrá también de estas variables: results_per_page: El número de objetos por página. no en cero. El nombre de la variable viene determinado por el parámetro template_object_name. el contexto de la plantilla tendrá los object_list: La lista de los objetos. valdrá False si el número de objetos disponibles es inferior o igual a paginate_by. page es un índice basado en 1. hits: El número total de objetos en todas las páginas. La etiqueta de aplicación es el nombre de la aplicación en que se ha denido el modelo. Incluso si no hubiera siguiente página. Puedes indicar qué pagina page en el URLconf. lo que signica que la primera página siempre será la número 1.2. También utiliza un índice basado en 1.3. no sólo en la actual.

conf. se usaría el indicado por el argumento Es un mecanismo un poco enmarañado. ) Argumentos obligatorios queryset: Un QuerySet que será usado para localizar el objeto a mostrar (véase la Tabla D-1). list_detail.views. pero puede ser de mucha ayuda en algunos Esta vista también acepta estos argumentos comunes (Véase la tabla D-1): context_processors extra_context mimetype template_loader template_name template_object_name Nombre de la plantilla Si no se especican template_name ni template_name_field. y no se debe usar si estás usando el argumento object_id. hay que emplear obligatoriamente el argumento slug_field (que se explica en la siguiente sección).objects.objects. casos.models import Author from django.urls. y luego hace falta. En otras palabras.generic import list_detail author_list_info = { 'queryset' : Author. slug: La etiqueta o slug del objeto en cuestión.3. author_list_info). o un: object_id: o bien: El valor de la clave primaria del objeto a mostrar. y denes genérica de este objeto template_name.defaults import * from django. (r'authors/$'. 'allow_empty': True. Es obligatorio si estás usando el argumento slug. entonces la vista usará como plantilla 'foo.object_detail. si tu objeto tiene un atributo to 'foo. Si se usa este sistema de identicación.html'. "template_object_name" : "author". } author_detail_info = { "queryset" : Author. list_detail. por template_name_field no existe.object_list. puedes almacenar en tu objeto la plantilla a usar. Si el atributo indicado 'the_template' que contiene la cadena de textemplate_name_field para que valga 'the_template'. Argumentos opcionales slug_field: El nombre del atributo del objeto que contiene el slug. template_name_field: El nombre de un atributo del objeto cuyo valor se usará como el nombre de la plantilla a utilizar. } urlpatterns = patterns(''.books.ht revisión 789 del 9 de noviembre de 2009 . podemos añadir una vista de detalle de cada autor modicacando el URLconf de la siguiente manera: from mysite.html'. VISTAS DE LISTADO/DETALLE 301 Ejemplo Siguiendo con el ejemplo anterior.all(). (r'^authors/(?P<object_id>d+)/$'. author_detail_info).all().D. se usará la plantilla <app_label>/<model_name>_detail. De esta forma.

generic import date_based book_info = { "queryset" : Book.objects.archive_index. podemos denir el argumento (o permitir a los usuarios visitar páginas de archivo en el futuro).defaults import * from django. Esto te permite publicar objetos por adelantado.302 APÉNDICE D.conf. revisión 789 del 9 de noviembre de 2009 . Para estas vistas.books. django. un calendario de próximos eventos). Sin embargo.models import Book from django.views.all(). los más recientes) según la Ejemplo Supongamos el típico editor que desea una página con la lista de sus últimos libros publicados. o el archivo de una bitácora o blog. Suponiendo archive_index que tenemos un objeto Book con un atributo de fecha de publicación. el contexto de la plantilla tendrá los object: El objeto. REFERENCIA DE LAS VISTAS GENÉRICAS Contexto de plantilla Además de los valores que se puedan haber denido en siguientes valores: extra_context.date_based. incluso aunque hubiera objetos con esa fecha en el sistema.generic. (r'^books/$'.4. Django mostrará automáticamente un error 404 (Página no encontrada). La vista usará los valores de ese campo como referencia para obtener los últimos objetos. publication_date. este comportamiento no es el deseable (por ejemplo. para otros tipos de objetos con fechas. Esto signica que si intentas visitar una página del archivo que esté en el futuro. el nombre de la variable será foo. si se ha especicado el argumento denimos template_object_name D. El nombre de esta variable puede ser distinto template_object_name.1. "date_field" : "publication_date" } urlpatterns = patterns(''. book_info). Truco: En principio. Si como 'foo'. ) Argumentos obligatorios date_field: El nombre de un campo DateField o DateTimeField de los objetos que componen el QuerySet. estas vistas ignoran las fechas que estén situadas en el futuro. date_based.views. cuyo valor es 'object' por defecto. queryset: El QuerySet de objetos que forman el archivo. Vistas genéricas basadas en fechas Estas vistas genéricas basadas en fechas se suelen utilizar para organizar la parte de archivo de nuestro contenido. allow_future como True y de esa manera conseguir que los objetos con fechas futuras aparezcan D. podemos usar la vista para resolver este problema: from mysite. Índice de archivo Vista a importar : fecha.archive_index Esta vista proporciona un índice donde se mostraría los últimos objetos (es decir.urls. que no se mostrarán públicamente hasta que se llegue a la fecha de publicación deseada.4. Los casos típicos son los archivos por año/mes/día de un periódico.

models import Book from django. Su valor por defecto es 15.4. si num_latest vale 10. los años mas recientes Por ejemplo. D. ) revisión 789 del 9 de noviembre de 2009 . Esta vista también acepta estos argumentos comunes (Véase la tabla D-1): allow_empty context_processors extra_context mimetype template_loader template_name Nombre de la plantilla Si no se ha especicado template_name.generic.urls.archive_year Esta vista sirve para presentar archivos basados en años. Una lista de objetos de tipo hay objetos.date. de acuerdo al queryset.4.2.archive_year.date que representarían todos los años en los que Vienen ordenados de forma descendente.html. (r'^books/(?P<year>d{4})/?$'. para un blog que tuviera entradas desde el año 2003 hasta el 2006. uno para cada uno se esos años.archive_index.views. VISTAS GENÉRICAS BASADAS EN FECHAS 303 Argumentos opcionales allow_future: Un valor booleano que indica si los objetos futuros en el futuro) deben aparecer o no. el contexto de la plantilla tendrá los date_list: primero. con fecha de referencia num_latest: El número de objetos que se deben enviar a la plantilla. datetime. Contexto de la plantilla Además de los valores que se puedan haber denido en siguientes valores: extra_context.generic import date_based book_info = { "queryset" : Book.defaults import * from django.books. (es decir. se usará la plantilla <app_label>/<model_name>_archive.objects. book_info). date_based. date_based.D. latest: Los últimos diente por el campo será una lista de los num_latest objetos en el sistema. Archivos anuales Vista a importar : django. "date_field" : "publication_date" } urlpatterns = patterns(''.date_based. la lista contendrá cuatro objetos de tipo datetime. Por ejemplo. (r'^books/$'.all(). entonces latest últimos 10 objetos contenidos en el queryset.views. Poseen una lista de los meses en los que hay algún objeto. y pueden mostrar opcionalmente todos los objetos publicados en un año determinado. Ejemplo Vamos a ampliar el ejemplo anterior incluyendo una vista que muestre todos los libros publicados en un determinado año: from mysite.conf. considerándolos ordenados de forma descendate_field de referencia. book_info).

Si make_object_list es False. que representan todos los meses en los que hay disponibles objetos en un año determinado. véase la información sobre object_list en la siguiente explicación sobre Contexto de plantilla). la vista usará la plantilla <app_label>/<model_name>_archive_year. QuerySet de objetos archivados. del parámetro D. normalmente se obtiene de un parámetro en la URL). object_list será una lista vacía. Contexto de la plantilla Además de los valores que se puedan haber denido en siguientes valores: extra_context. allow_future: futuro. que la vista usará para mostrar el archivo (Como se ve en el ejemplo. la lista de objetos estará disponible para la plantilla con el nombre de object_list (Aunque este nombre podría ser diferente.3. ordenados por fecha. Esta vista también acepta los siguientes argumentos comunes (Véase la Tabla D-1): Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el allow_empty context_processors extra_context mimetype template_loader template_name template_object_name Nombre de la plantilla Si no se especica ningún valor en template_name. en forma de cadena de texto con cuatro dígitos.date. revisión 789 del 9 de noviembre de 2009 . El nombre de la variable depende template_object_name.html. REFERENCIA DE LAS VISTAS GENÉRICAS Argumentos obligatorios date_field: queryset: El Igual que en archive_index (Véase la sección previa).generic. Argumentos opcionales make_object_list: Un valor booleano que indica si se debe obtener la lista completa de objetos para este año y pasársela a la plantilla.4. Si template_object_name fuera 'foo'. el contexto de la plantilla tendrá los date_list: ascendente. Archivos mensuales Vista a importar : django.date_based. de acuerdo al contenido del queryset. que es 'object' por defecto. con cuatro dígitos. Si es True. en orden year: El año a mostrar. Una lista de objetos de tipo datetime.archive_month Esta vista proporciona una representación basada en meses. Su valor por defecto es False.views.304 APÉNDICE D. year: El año. object_list: Si el parámetro make_object_list es True. el nombre de esta variable sería foo_list. esta variable será una lista de objetos cuya fecha de referencia cae en en año a mostrar. en la que se muestran todos los objetos cuya fecha de referencia caiga en un determinado mes y año.

la vista usará como plantilla <app_label>/<model_name>_archive_mon Contexto de la plantilla Además de los valores que se puedan haber denido en siguientes valores: extra_context. Argumentos opcionales usado para el month_format: es Una cadena de texto que determina el formato que debe usar el parámetro sintaxis a usar debe coincidir con la de la función se puede consultar en " %b". month: El mes a mostrar. El nombre de la variable depende del parámetro template_object_name. date_field: El nombre del campo de tipo DateField o DateTimeField en el modelo QuerySet que se usará como fecha de referencia.archive_year. Para cambiarlo de forma que se usen números. Si template_object_name fuera 'foo'.date que representa el primer día del mes anterior.date que representa el mes y año de referencia.archive_index. date_based. Si el siguiente mes cae en el futuro. previous_month: Un objeto de tipo datetime. que signica el nombre del mes. añadir una vista mensual debería ser algo sencillo: urlpatterns = patterns(''. queryset: El QuerySet de objetos archivados.date que representa el primer día del siguiente mes. y abreviado a tres letras (Es decir.archive_month. La time.D. month.strftime (La documentación de esta función http://www. igual al que hemos visto en otras vistas anteriores. book_info).4. jan. (r'^books/(?P<year>d{4})/?$'. ( r'^(?P<year>d{4})/(?P<month>[a-z]{3})/$'. valdrá None. VISTAS GENÉRICAS BASADAS EN FECHAS 305 Ejemplo Siguiendo con nuestro ejemplo. en inglés. object_list: Una lista de objetos cuya fecha de referencia cae en en año y mes a mostrar. ) Argumentos obligatorios year: El año a mostrar. date_based. book_info ). Esta vista también acepta los siguientes argumentos comunes (Véase la Tabla D-1): allow_empty context_processors extra_context mimetype template_loader template_name template_object_name Nombre de la plantilla Si no se especica ningún valor en template_name. que es 'object' por defecto. date_based. (r'^books/$'. el contexto de la plantilla tendrá los month: Un objeto de tipo datetime. hay que utilizar como cadena de formato allow_future: Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el futuro.). etc. book_info).djangoproject. Su valor por defecto " %m". feb. Al contrario que next_month. en forma de cadena de texto con cuatro dígitos.com/r/python/strftime/). su valor nunca será None. next_month: Un objeto de tipo datetime. revisión 789 del 9 de noviembre de 2009 . el nombre de esta variable sería foo_list. formateado de acuerdo con el argumento month_format.

que es 'object' por defecto. ( r'^(?P<year>d{4})/(?P<week>d{2})/$'. el contexto de la plantilla tendrá los week: Un objeto de tipo datetime.date_based. La semana del año (Una cadena de texto).views. book_info ).date. el nombre de esta variable sería foo_list. Ejemplo urlpatterns = patterns(''.306 APÉNDICE D. # . revisión 789 del 9 de noviembre de 2009 . Nota Por consistencia con las bibliotecas de manejo de fechas de Python. Django asume que el primer día de la semana es el domingo. cuyo valor es el primer día de la semana considerada.archive_week Esta vista muestra todos los objetos de una semana determinada.4. date_based. Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el Esta vista también acepta los siguientes argumentos comunes (Véase la Tabla D-1): allow_empty context_processors extra_context mimetype template_loader template_name template_object_name Nombre de la plantilla Si no se ha especicado ningún valor en template_name la vista usará como plantilla <app_label>/<model_name>_archive_week Contexto de la plantilla Además de los valores que se puedan haber denido en siguiente