Django, La Guia Definitiva
Django, La Guia Definitiva
Django, La Guia Definitiva
Versin
ndice general
1. EL LIBRO DE DJANGO 1.1. Getting help . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1.2. CAPITULOS: . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2. Captulo 1: Introduccin a Django 2.1. Qu es un Framework Web? 2.2. El patrn de diseo MVC . . 2.3. La historia de Django . . . . 2.4. Cmo leer este libro . . . . . 2.5. Qu sigue? . . . . . . . . . . 3. Captulo 2: Empezando 3.1. Instalar Python . . . . . . . 3.2. Instalar Django . . . . . . . 3.3. Congurar la base de datos 3.4. Comenzar un proyecto . . 3.5. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1 1 1 3 3 4 6 7 8 9 9 9 11 12 14 15 15 16 18 21 21 22 25 27 29 29 30 37 42 43 44 49 52
4. Captulo 3: Los principios de las pginas Web dinmicas 4.1. Tu primera Vista: Contenido dinmico . . . . . . . . 4.2. Mapeando URLs a Vistas . . . . . . . . . . . . . . . . 4.3. Cmo procesa una peticin Django . . . . . . . . . . 4.4. URLconfs y el acoplamiento dbil . . . . . . . . . . . 4.5. Errores 404 . . . . . . . . . . . . . . . . . . . . . . . . 4.6. Tu segunda Vista: URLs dinmicas . . . . . . . . . . . 4.7. Pginas de error bonitas con Django . . . . . . . . . . 4.8. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . 5. Captulo 4: El sistema de plantillas de Django 5.1. Sistema bsico de plantillas . . . . . . . . 5.2. Empleo del sistema de plantillas . . . . . 5.3. Etiquetas bsicas de plantillas y ltros . . 5.4. Filosofa y Limitaciones . . . . . . . . . . 5.5. Uso de plantillas en las vistas . . . . . . . 5.6. Cargadores de plantillas . . . . . . . . . . 5.7. Herencia de plantillas . . . . . . . . . . . 5.8. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
6. Captulo 5: Interactuar con una base de datos: Modelos 6.1. La manera tonta de hacer una consulta a la base de datos en las vistas 6.2. El patrn de diseo MTV . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.3. Conguracin de la base de datos . . . . . . . . . . . . . . . . . . . . . . 6.4. Tu primera aplicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.5. Denir modelos en Python . . . . . . . . . . . . . . . . . . . . . . . . . . 6.6. Tu primer modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.7. Instalar el modelo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.8. Acceso bsico a datos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.9. Agregar strings de representacin del modelo . . . . . . . . . . . . . . . 6.10. Insertar y actualizar datos . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.11. Seleccionar objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.12. Eliminar objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6.13. Realizar cambios en el esquema de una base de datos . . . . . . . . . . . 6.14. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7. Captulo 6: El sitio de Administracin Django 7.1. Activar la interfaz de administracin . . . . . . . . . . . . 7.2. Usar la interfaz de administracin . . . . . . . . . . . . . . 7.3. Personalizar la interfaz de administracin . . . . . . . . . 7.4. Personalizar la apariencia de la interfaz de administracin 7.5. Personalizar la pgina ndice del administrador . . . . . . 7.6. Cundo y porqu usar la interfaz de administracin . . . . 7.7. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . 8. Captulo 7: Procesamiento de formularios 8.1. Bsquedas . . . . . . . . . . . . . . . . . . . . 8.2. El formulario perfecto . . . . . . . . . . . . 8.3. Creacin de un formulario para comentarios 8.4. Procesamiento de los datos suministrados . 8.5. Nuestras propias reglas de validacin . . . . 8.6. Una presentacin personalizada . . . . . . . 8.7. Creando formularios a partir de Modelos . . 8.8. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . .
53 53 54 55 57 58 59 61 63 64 65 66 70 70 73 75 75 76 84 86 86 86 87 89 89 91 92 94 96 96 97 98
9. Captulo 8: Vistas avanzadas y URLconfs 99 9.1. Trucos de URLconf . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99 9.2. Incluyendo otras URLconfs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110 9.3. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 112 10. Captulo 9: Vistas genricas 10.1. Usar vistas genricas . . . . . 10.2. Vistas genricas de objetos . 10.3. Extender las vistas genricas 10.4. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113 113 114 116 120 121 121 122 126 127 136 137 138 138
11. Captulo 10: Extender el sistema de plantillas 11.1. Revisin del lenguaje de plantillas . . . . . . . . . . . . 11.2. Procesadores de contexto . . . . . . . . . . . . . . . . . 11.3. Detalles internos de la carga de plantillas . . . . . . . . 11.4. Extender el sistema de plantillas . . . . . . . . . . . . . 11.5. Escribir cargadores de plantillas personalizados . . . . 11.6. Usar la referencia de plantillas incorporadas . . . . . . 11.7. Congurar el sistema de plantillas en modo autnomo 11.8. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . .
12. Captulo 11: Generacin de contenido no HTML 139 12.1. Lo bsico: Vistas y tipos MIME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 139 12.2. Produccin de CSV . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 140
II
Generar PDFs . . . . . . . . . . . . . . . Otras posibilidades . . . . . . . . . . . . El Framework de Feeds de Sindicacin El framework Sitemap . . . . . . . . . . Qu sigue? . . . . . . . . . . . . . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
141 143 144 149 153 155 155 158 162 163 172 174 177 177 181 181 183 184 187 187 187 189 189 190 195 198 199 201 203 203 205 205 206 206 208 211 213 213 215 217 217 219 220 221 223 226 226 227 228 230 231 233
13. Captulo 12: Sesiones, usuario e inscripciones 13.1. Cookies . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13.2. El entorno de sesiones de Django . . . . . . . . . . . . . . . 13.3. Usuarios e identicacin . . . . . . . . . . . . . . . . . . . 13.4. Utilizando usuarios . . . . . . . . . . . . . . . . . . . . . . 13.5. El resto de detalles: permisos, grupos, mensajes y perles 13.6. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . 14. Captulo 13: Cache 14.1. Activar el Cache . . . . . . . . . . . . 14.2. La cache por sitio . . . . . . . . . . . . 14.3. Cache por vista . . . . . . . . . . . . . 14.4. La API de cache de bajo nivel . . . . . 14.5. Caches upstream . . . . . . . . . . . . 14.6. Otras optimizaciones . . . . . . . . . . 14.7. Orden de MIDDLEWARE_CLASSES 14.8. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
15. Captulo 14: Otros sub-frameworks contribuidos 15.1. La biblioteca estndar de Django . . . . . . . 15.2. Sites . . . . . . . . . . . . . . . . . . . . . . . 15.3. Flatpages . . . . . . . . . . . . . . . . . . . . 15.4. Redirects . . . . . . . . . . . . . . . . . . . . . 15.5. Proteccin contra CSRF . . . . . . . . . . . . 15.6. Hacer los datos ms humanos . . . . . . . . 15.7. Filtros de marcado . . . . . . . . . . . . . . . 15.8. Qu sigue? . . . . . . . . . . . . . . . . . . . 16. Captulo 15: Middleware 16.1. Qu es middleware . . . . . 16.2. Instalacin de Middleware 16.3. Mtodos de un Middleware 16.4. Middleware incluido . . . . 16.5. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
17. Captulo 16: Integracin con Base de datos y Aplicaciones existentes 17.1. Integracin con una base de datos existente . . . . . . . . . . . . 17.2. Integracin con un sistema de autenticacin . . . . . . . . . . . 17.3. Integracin con aplicaciones web existentes . . . . . . . . . . . . 17.4. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 18. Captulo 17: Extender la Interfaz de Administracin de Django 18.1. El Zen de la aplicacin Admin . . . . . . . . . . . . . . . . . 18.2. Pesonalizar las plantillas de la interfaz . . . . . . . . . . . . 18.3. Crear vistas de administracin personalizadas . . . . . . . . 18.4. Sobreescribir vistas incorporadas . . . . . . . . . . . . . . . . 18.5. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19. Captulo 18: Internacionalizacin 19.1. Especicando cadenas de traduccin en cdigo Python . . . 19.2. Especicando cadenas de traduccin en cdigo de plantillas 19.3. Creando archivos de idioma . . . . . . . . . . . . . . . . . . 19.4. Cmo descubre Django la preferencia de idioma . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
III
La vista de redireccin set_language . . . . . . . . Usando traducciones en tus propios proyectos . . Traducciones y JavaScript . . . . . . . . . . . . . . Notas para usuarios familiarizados con gettext Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . .
235 236 236 238 238 239 239 240 241 242 243 244 245 246 247 247 249 250 251 251 255 260 264 266 267 269
20. Captulo 19: Seguridad 20.1. El tema de la seguridad en la Web 20.2. Inyeccin de SQL . . . . . . . . . . 20.3. Cross-Site Scripting (XSS) . . . . . 20.4. Cross-Site Request Forgery . . . . 20.5. Session Forging/Hijacking . . . . 20.6. Inyeccin de cabeceras de email . 20.7. Directory Traversal . . . . . . . . . 20.8. Exposicin de mensajes de error . 20.9. Palabras nales sobre la seguridad 20.10. Qu sigue? . . . . . . . . . . . . .
21. Captulo 20: Puesta en marcha de Django en un servidor 21.1. Nada Compartido . . . . . . . . . . . . . . . . . . . . 21.2. Un nota sobre preferencias personales . . . . . . . . . 21.3. Usando Django con Apache y mod_python . . . . . . 21.4. Usando Django con FastCGI . . . . . . . . . . . . . . 21.5. Escalamiento . . . . . . . . . . . . . . . . . . . . . . . 21.6. Ajuste de Performance . . . . . . . . . . . . . . . . . . 21.7. Qu sigue? . . . . . . . . . . . . . . . . . . . . . . . . 22. Apndice 23. Inicio: 24. Apndice A: Casos de estudio 24.1. Elenco . . . . . . . . . . . 24.2. Por qu Django? . . . . . 24.3. Comenzando . . . . . . . 24.4. Portando cdigo existente 24.5. Cmo les fue? . . . . . . 24.6. Estructura de Equipo . . 24.7. Implementacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . . . . . . .
271 271 272 273 273 274 275 276 277 277 282 285 289 291 294 297 303 304 305 306 306 307 315 319 320 324
25. Apndice B: Referencia de la denicin de modelos 25.1. Campos . . . . . . . . . . . . . . . . . . . . . . . 25.2. Opciones Universales de Campo . . . . . . . . . 25.3. Relaciones . . . . . . . . . . . . . . . . . . . . . . 25.4. Opciones de los Metadatos del Modelo . . . . . 25.5. Managers . . . . . . . . . . . . . . . . . . . . . . 25.6. Mtodos de Modelo . . . . . . . . . . . . . . . . 25.7. Opciones del Administrador . . . . . . . . . . . 26. Apndice C: Referencia de la API de base de datos 26.1. Creando Objetos . . . . . . . . . . . . . . . . . 26.2. Grabando cambios de objetos . . . . . . . . . . 26.3. Recuperando objetos . . . . . . . . . . . . . . . 26.4. Caching y QuerySets . . . . . . . . . . . . . . . 26.5. Filtrando objetos . . . . . . . . . . . . . . . . . 26.6. Patrones de bsqueda . . . . . . . . . . . . . . 26.7. Bsquedas complejas con Objetos Q . . . . . . 26.8. Objetos Relacionados . . . . . . . . . . . . . . 26.9. Borrando Objectos . . . . . . . . . . . . . . . . . . . . . . . . .
IV
26.10. Mtodos de Instancia Adicionales . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 324 26.11. Atajos (Shortcuts) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 325 26.12. Utilizando SQL Crudo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 326 27. Apndice D: Referencia de las vistas genricas 27.1. Argumentos comunes a todas las vistas genricas 27.2. Vistas genricas simples . . . . . . . . . . . . . . . 27.3. Vistas de listado/detalle . . . . . . . . . . . . . . . 27.4. Vistas genricas basadas en fechas . . . . . . . . . 27.5. Vistas genericas para Crear/Modicar/Borrar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 327 327 328 329 333 341 345 345 347 348 349
28. Apndice E: Variables de conguracin 28.1. Qu es un archivo de conguracin . . . . . . . . . . . . . . . . . . . . . . . . 28.2. Indicando la conguracin: DJANGO_SETTINGS_MODULE . . . . . . . . . 28.3. Usando variables de conguracin sin jar DJANGO_SETTINGS_MODULE 28.4. Variables de conguracin disponibles . . . . . . . . . . . . . . . . . . . . . .
29. Apndice F: Etiquetas de plantilla y ltros predenidos 361 29.1. Etiquetas predenidas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 361 29.2. Filtros predenidos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 369 30. Apndice G: El utilitario django-admin 379 30.1. Uso . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 379 30.2. Acciones Disponibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 380 30.3. Opciones Disponibles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 385 31. Apndice H: Objetos Peticin y Respuesta 389 31.1. HttpRequest . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 389 31.2. HttpResponse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 394 32. Sobre el libro 33. Indices, glossary and tables 399 401
VI
CAPTULO 1
EL LIBRO DE DJANGO
1.2 CAPITULOS:
01.- INTRODUCCION A DJANGO 02.- EMPEZANDO 03.- LOS PRINCIPIOS DE LAS PAGINAS WEB DINAMICAS 04.- EL SISTEMA DE PLANTILLAS DE DJANGO 05.- INTERACTUAR CON UNA BASE DE DATOS 06.- EL SITIO DE ADMINISTRACION DE DJANGO 07.- PROCESAMIENTO DE FORMULARIOS 08.- VISTAS AV ANZADAS Y URLCONFS 09.- VISTAS GENERICAS 10.- EXTENDIENDO EL SISTEMA DE PLANTILLAS 11.- GENERACION DE CONTENIDO NO HTML 12.- SESIONES, USUARIOS E INSCRIPCIONES 13.- CACHE 14.- BATERIAS INCLUIDAS EN DJANGO 15.- MIDDLEWARE
16.- INTEGRACION CON BASE DE DATOS Y APLICACIONES EXISTENTES 17.- EXTENDIENDO LA INTERFAZ DE ADMINISTRACION DE DJANGO 18.- INTERNACIONALIZACION 19.- SEGURIDAD 20.- IMPLEMENTANDO DJANGO
CAPTULO 2
Este libro es sobre Django, un framework de desarrollo Web que ahorra tiempo y hace que el desarrollo Web sea divertido. Utilizando Django puedes crear y mantener aplicaciones Web de alta calidad con un mnimo esfuerzo. En el mejor de los casos, el desarrollo web es un acto entretenido y creativo; en el peor, puede ser una molestia repetitiva y frustrante. Django te permite enfocarte en la parte creativa la parte divertida de tus aplicaciones Web al mismo tiempo que mitiga el esfuerzo de las partes repetitivas. De esta forma, provee un alto nivel de abstraccin de patrones comunes en el desarrollo Web, atajos para tareas frecuentes de programacin y convenciones claras sobre cmo solucionar problemas. Al mismo tiempo, Django intenta no entrometerse, dejndote trabajar fuera del mbito del framework segn sea necesario. El objetivo de este libro es convertirte en un experto de Django. El enfoque es doble. Primero, explicamos en profundidad lo que hace Django, y cmo crear aplicaciones Web con l. Segundo, discutiremos conceptos de alto nivel cuando se considere apropiado, contestando la pregunta Cmo puedo aplicar estas herramientas de forma efectiva en mis propios proyectos? Al leer este libro, aprenders las habilidades necesarias para desarrollar sitios Web poderosos de forma rpida, con cdigo limpio y de fcil mantenimiento. En este captulo ofrecemos una visin general de Django.
conexion = MySQLdb.connect(user= yo , passwd= dejame_entrar , db= mi_base ) cursor = conexion.cursor() cursor.execute( " SELECT nombre FROM libros ORDER BY fecha_pub DESC LIMIT 10 " ) for fila in cursor.fetchall(): print " <li> %s </li> " % fila[0] print " </ul> " print " </body></html> " conexion.close()
Este cdigo es fcil de entender. Primero imprime una lnea de Content-Type, seguido de una lnea en blanco, tal como requiere CGI. Imprime HTML introductorio, se conecta a la base de datos y ejecuta una consulta que obtiene los diez libros ms recientes. Hace un bucle sobre esos libros y genera una lista HTML desordenada. Finalmente imprime el cdigo para cerrar el HTML y cierra la conexin con la base de datos. Con una nica pgina dinmica como esta, el enfoque desde cero no es necesariamente malo. Por un lado, este cdigo es sencillo de comprender incluso un desarrollador novato puede leer estas 16 lneas de Python y entender todo lo que hace, de principio a n . No hay ms nada que aprender; no hay ms cdigo para leer. Tambin es sencillo de utilizar: tan slo guarda este cdigo en un archivo llamado ultimoslibros.cgi, sube ese archivo a un servidor Web y visita esa pgina con un navegador. Pero a medida que una aplicacin Web crece ms all de lo trivial, este enfoque se desmorona y te enfrentas a una serie de problemas: Qu sucede cuando mltiples pginas necesitan conectarse a la base de datos? Seguro que ese cdigo de conexin a la base de datos no debera estar duplicado en cada uno de los scripts CGI, as que la forma pragmtica de hacerlo sera refactorizarlo en una funcin compartida. Debera un desarrollador realmente tener que preocuparse por imprimir la lnea de ContentType y acordarse de cerrar la conexin con la base de datos? Este tipo de cdigo repetitivo reduce la productividad del programador e introduce la oportunidad para que se cometan errores. Estas tareas de conguracin y cierre estaran mejor manejadas por una infraestructura comn. Qu sucede cuando este cdigo es reutilizado en mltiples entornos, cada uno con una base de datos y contraseas diferentes? En ese punto, se vuelve esencial alguna conguracin especca del entorno. Qu sucede cuando un diseador Web que no tiene experiencia programando en Python desea redisear la pgina? Lo ideal sera que la lgica de la pgina la bsqueda de libros en la base de datos est separada del cdigo HTML de la pgina, de modo que el diseador pueda hacer modicaciones sin afectar la bsqueda. Precisamente estos son los problemas que un framework Web intenta resolver. Un framework Web provee una infraestructura de programacin para tus aplicaciones, para que puedas concentrarte en escribir cdigo limpio y de fcil mantenimiento sin tener que reinventar la rueda. En resumidas cuentas, eso es lo que hace Django.
# views.py (la parte lgica) from django.shortcuts import render_to_response from models import Book def latest_books(request): book_list = Book.objects.order_by( -pub_date )[:10] return render_to_response( latest_books.html , { book_list : book_list}) # urls.py (la configuracin URL) from django.conf.urls.defaults import * import views urlpatterns = patterns( , ( r latest/$ , views.latest_books), ) # latest_books.html (la plantilla) <html><head><title>Books</title></head> <body> <h1>Books</h1> <ul> {% for book in book_list %} <li>{{ book.name }}</li> {% endfor %} </ul> </body></html>
Todava no es necesario preocuparse por los detalles de cmo funciona esto tan slo queremos que te acostumbres al diseo general . Lo que hay que notar principalmente en este caso son las cuestiones de separacin: El archivo models.py contiene una descripcin de la tabla de la base de datos, como una clase Python. A esto se lo llama el modelo. Usando esta clase se pueden crear, buscar, actualizar y borrar entradas de tu base de datos usando cdigo Python sencillo en lugar de escribir declaraciones SQL repetitivas. El archivo views.py contiene la lgica de la pgina, en la funcin latest_books(). A esta funcin se la denomina vista. El archivo urls.py especica qu vista es llamada segn el patrn URL. En este caso, la URL /latest/ ser manejada por la funcin latest_books(). El archivo latest_books.html es una plantilla HTML que describe el diseo de la pgina. Tomadas en su conjunto, estas piezas se aproximan al patrn de diseo Modelo-Vista-Controlador (MVC). Dicho de manera ms fcil, MVC dene una forma de desarrollar software en la que el cdigo para denir y acceder a los datos (el modelo) est separado del pedido lgico de asignacin de ruta (el controlador), que a su vez est separado de la interfaz del usuario (la vista). Una ventaja clave de este enfoque es que los componentes tienen un acoplamiento dbil (N. de T.: por loosely coupled) entre s. Eso signica que cada pieza de la aplicacin Web que funciona sobre Django tiene un nico propsito clave, que puede ser modicado independientemente sin afectar las otras piezas. Por ejemplo, un desarrollador puede cambiar la URL de cierta parte de la aplicacin sin afectar la implementacin subyacente. Un diseador puede cambiar el HTML de una pgina sin tener que tocar el cdigo Python que la renderiza. Un administrador de base de datos puede renombrar una tabla de la base de datos y especicar el cambio en un nico lugar, en lugar de tener que buscar y reemplazar en varios archivos. En este libro, cada componente tiene su propio captulo. Por ejemplo, el Captulo 3 trata sobre las vistas, el Captulo 4 sobre las plantillas, y el Captulo 5 sobre los modelos. El Captulo 5 profundiza tambin en 2.2. El patrn de diseo MVC 5
desarrolladores del framework tienen un alto grado de inters en asegurarse de que Django les ahorre tiempo a los desarrolladores, produzca aplicaciones que son fciles de mantener y rindan bajo mucha carga. Aunque existan otras razones, los desarrolladores estn motivados por sus propios deseos egostas de ahorrarse tiempo a ellos mismos y disfrutar de sus trabajos. (Para decirlo sin vueltas, se comen su propia comida para perros).
2.5 Qu sigue?
En el Captulo 2, a continuacin, empezaremos con Django, explicando su instalacin y conguracin inicial.
CAPTULO 3
Captulo 2: Empezando
Creemos que es mejor empezar con fuerza. En los captulos que le siguen a este descubrirs los detalles y el alcance del framework Django, pero por ahora, confa en nosotros, este captulo es divertido. Instalar Django es fcil. Django se puede usar en cualquier sistema que corra Python, por eso es posible instalarlo de varias maneras. En este captulo explicamos las situaciones ms comunes de instalacin de Django. El Captulo 20 explica cmo utilizar Django en produccin.
Si ves un error como: ommand not found" u .orden no encontrada", tienes que bajar e instalar Python. Fjate en http://www.python.org/download/ para empezar. La instalacin es rpida y fcil.
4. sudo python setup.py install En Windows, recomendamos usar 7-Zip para manejar archivos comprimidos de todo tipo, incluyendo .tar.gz. Puedes bajar 7-Zip de http://www.djangoproject.com/r/7zip/. Cambia a algn otro directorio e inicia python. Si todo est funcionando bien, deberas poder importar el mdulo django:
>>> import django >>> django.VERSION (TU VERSION)
Nota: El intrprete interactivo de Python es un programa de lnea de comandos que te permite escribir un programa Python de forma interactiva. Para iniciarlo slo ejecuta el comando python en la lnea de comandos. Durante todo este libro, mostraremos ejemplos de cdigo Python como si estuviesen escritos en el intrprete interactivo. El triple signo de mayor que (>>>) es el prompt de Python.
3. Crea site-packages/django.pth y agrega el directorio djtrunk a este, o actualiza tu PYTHONPATH agregando djtrunk. 4. Incluye djtrunk/django/bin en el PATH de tu sistema. Este directorio incluye utilidades de administracin como django-admin.py. Consejo: Si los archivo .pth son nuevos para ti, puedes http://www.djangoproject.com/r/python/site-module/. aprender ms de ellos en
Luego de descargarlo desde Subversion y haber seguido los pasos anteriores, no necesitas ejecutar python setup.py install Acabas de hacer este trabajo a mano! Debido a que el trunk de Django cambia a menudo corrigiendo bugs y agregando funcionalidades, probablemente quieras actualizarlo con frecuencia a cada hora, si eres un obsesivo. Para actualizar el cdigo, solo ejecuta el comando svn update desde el directorio djtrunk. Cuando ejecutes este comando, Subversion contactar http://code.djangoproject.com, determinar si el cdigo ha cambiado, y actualizar tu versin local del cdigo con cualquier cambio que se haya hecho desde la ltima actualizacin. Es muy bueno.
10
No es lo mismo que ves ? Si estamos viendo un arbol de directorios diferentes al anterior problablemente estamos usando una version de Django anterior. Estos archivos son los siguientes: mysite/: El Directorio que contiene nuestro projecto. Podemos cambiarle el nombre en cualquier momento sin afectar nuestro proyecto. manage.py: Una utilidad de lnea de comandos que te deja interactuar con este proyecto de Django de varias formas.
12
mysite/mysite/:El directorio de nuestro paquete que contiene nuestro projecto el cual es un paquete python y el que se usara para importar cualquier cosa dentro de el. mysite/__init__.py: Un archivo requerido para que Python trate a este directorio como un paquete (i.e. un grupo de mdulos). mysite/settings.py: Opciones/conguraciones para este proyecto de Django. mysite/urls.py: La declaracin de las URL para este proyecto de Django; una tabla de contenidos de tu sitio hecho con Django. mysite/wsgi.py: El archivo encargado de ser compatible con el servidor web.
Aunque el servidor de desarrollo es extremadamente til para, bueno, desarrollar, resiste la tentacin de usar este servidor en cualquier entorno parecido a produccin. El servidor de desarrollo puede manejar ablemente una sola peticin a la vez, y no ha pasado por una auditora de seguridad de ningn tipo. Cuando sea el momento de lanzar tu sitio, mira el Captulo 20 para informacin sobre cmo hacerlo con Django. Cambiar el host o el puerto Por defecto, el comando runserver inicia el servidor de desarrollo en el puerto 8000, escuchando slo conexiones locales. Si quieres cambiar el puerto del servidor, pasa este como un argumento de lnea de comandos:
python manage.py runserver 8080
Tambin puedes cambiar las direcciones de IP que escucha el servidor. Esto es utilizado especialmente si quieres compartir el desarrollo de un sitio con otros desarrolladores. Lo siguiente:
python manage.py runserver 0.0.0.0:8080
har que Django escuche sobre cualquier interfaz de red, permitiendo que los dems equipos puedan conectarse al servidor de desarrollo.
13
Ahora que el servidor est corriendo, visita http://127.0.0.1:8000/ con tu navegador web. Vers una pgina de Bienvenido a Django sombreada con un azul pastel agradable. Funciona!
3.5 Qu sigue?
Ahora que tienes todo instalado y el servidor de desarrollo corriendo, en el prximo captulo escribirs algo de cdigo bsico que muestra cmo servir pginas Web usando Django.
14
CAPTULO 4
En el captulo anterior, explicamos cmo crear un proyecto en Django y cmo poner en marcha el servidor de desarrollo de Django. Por supuesto, el sitio no hace nada til todava slo muestra el mensaje It worked!. Cambiemos eso. Este captulo presenta cmo crear paginas web dinmicas con Django.
Repasemos el cdigo anterior lnea a lnea: Primero, importamos la clase HttpResponse, la cual pertenece al mdulo django.http. Para ver ms detalles de los objetos HttpRequest y HttpResponse puedes consultar el Apndice H. Luego importamos el mdulo datetime de la biblioteca estndar de Python, el conjunto de mdulos tiles que vienen con Python. El mdulo datetime contiene varias funciones y clases para trabajar con fechas y horas, incluyendo una funcin que retorna la hora actual.
15
A continuacin, denimos la funcin llamada current_datetime. Esta es una funcin de vista. Cada funcin de vista toma como primer argumento un objeto HttpRequest, al que tpicamente se le asigna el nombre request. Nota que el nombre de la funcin de vista no importa; no tiene que ser nombrada de una determinada manera para que Django la reconozca. La llamamos current_datetime aqu, porque el nombre indica claramente lo que hace, pero se podra llamar super_duper_awesome_current_time, o algo ms repugnante. A Django no le interesa. La siguiente seccin explica cmo Django encuentra esta funcion. La primera lnea de cdigo dentro de la funcin calcula la fecha/hora actual, como un objeto datetime.datetime, y almacena el resultado en la variable local now. La segunda lnea de cdigo dentro de la funcin construye la respuesta HTML usando el formato de cadena de caracteres de Python. El %s dentro de la cadena de caracteres es un marcador de posicin, y el signo porcentaje despus de la cadena de caracteres, signica Reemplaza el %s por el valor de la variable now.. (S, el HTML es invlido, pero estamos tratando de mantener el ejemplo simple y corto) Por ltimo, la vista retorna un objeto HttpResponse que contiene la respuesta generada. Cada funcin de vista es responsable de retornar un objeto HttpResponse. (Hay excepciones, pero lo haremos ms adelante) Zona Horaria de Django Django incluye una opcin TIME_ZONE que por omisin es America/Chicago. Probablemente no es donde vivas, por lo que puedes cambiarlo en tu settings.py. Vese el Apndice E para ms detalles.
16
Cuando ejecutaste django-admin.py startproject en el captulo anterior, el script cre automticamente una URLconf por t: el archivo urls.py. Editemos ese archivo. Por omisin, se ver como:
from django.conf.urls.defaults import * urlpatterns = patterns( , # Example: # (r^mysite/, include(mysite.apps.foo.urls.foo)), # Uncomment this for admin: (r^admin/, include(django.contrib.admin.urls)),
# )
Repasemos el cdigo anterior lnea a lnea: La primera lnea importa todos los objetos desde el mdulo django.conf.urls.defaults, incluyendo una funcin llamada patterns. La segunda lnea llama a la funcin patterns() y guarda el resultado en una variable llamada urlpatterns. La funcin patterns() slo recibe un argumento la cadena de caracteres vaca. El resto de las lneas estn comentadas. (La cadena de caracteres puede ser usada para proveer un prejo comn para las funciones de vista, pero dejemos este uso ms avanzado para ms adelante). Lo principal que debemos notar aqu es la variable urlpatterns, la cual Django espera encontrar en tu mdulo ROOT_URLCONF. Esta variable dene el mapeo entre las URLs y el cdigo que manejan esas URLs. Por defecto, todo lo que est en URLconf est comentado tu aplicacin de Django es una pizarra blanca. (Como nota adicional, esta es la forma en la que Django saba que deba mostrar la pgina It worked! en el captulo anterior. Si la URLconf esta vaca, Django asume que acabas de crear el proyecto, por lo tanto, muestra ese mensaje). Editemos este archivo para exponer nuestra vista current_datetime:
from django.conf.urls.defaults import * from mysite.views import current_datetime urlpatterns = patterns( , ( r ^time/$ , current_datetime), )
Hicimos dos cambios aqu. Primero, importamos la vista current_datetime desde el mdulo (mysite/views.py, que en la sintaxis de import de Python se traduce a mysite.views). Luego, agregamos la lnea (r^time/$, current_datetime),. Esta lnea hace referencia a un URLpattern es una tupla de Python en dnde el primer elemento es una expresin regular simple y el segundo elemento es la funcin de vista que usa para ese patrn. En pocas palabras, le estamos diciendo a Django que cualquier peticin a la URL /time ser manejada por la funcin de vista current_datetime. Algunas cosas que vale la pena resaltar: Notemos que, en este ejemplo, pasamos la funcin de vista current_datetime como un objeto sin llamar a la funcin. Esto es una caracterstica de Python (y otros lenguajes dinmicos): las funciones son objetos de primera clase, lo cual signica que puedes pasarlas como cualquier otra variable. Qu bueno!, no? La r en r^time/$ signica que ^time/$ es una cadena de caracteres en crudo de Python. Esto permite que las expresiones regulares sean escritas sin demasiadas sentencias de escape. Puedes excluir la barra al comienzo de la expresin ^time/$ para que coincida con /time/. Django automticamente agrega una barra antes de toda expresin. A primera vista esto parece raro, pero una URLconf puede ser incluida en otra URLconf, y el dejar la barra de lado simplica mucho las cosas. Esto se retoma en el Captulo 8.
17
El caracter acento circunejo (^) y el carcter signo de dlar ($) son importantes. El acento circunejo signica que requiere que el patrn concuerde con el inicio de la cadena de caracteres, y el signo de dlar signica que exige que el patrn concuerde con el n de la cadena. Este concepto se explica mejor con un ejemplo. Si hubiramos utilizado el patrn ^time/ (sin el signo de dlar al nal), entonces cualquier URL que comience con time/ concordara, as como /time/foo y /time/bar, no slo /time/. Del mismo modo, si dejamos de lado el carcter acento circunejo inicial (time/$), Django concordara con cualquier URL que termine con time/, as como /foo/bar/time/. Por lo tanto, usamos tanto el acento circunejo como el signo de dlar para asegurarnos que slo la URL /time/ concuerde. Nada ms y nada menos. Quizs te preguntes qu pasa si alguien intenta acceder a /time. Esto es manejado como esperaras (a travs de un redireccionamiento) siempre y cuando APPEND_SLASH tenga asignado el valor True. (Vese el Apndice E para una buena lectura sobre este tema). Para probar nuestro cambios en la URLconf, inicia el servidor de desarrollo de Django, como hiciste en el Captulo 2, ejecutando el comando python manage.py runserver. (Si ya lo tenas corriendo, est bien tambin. El servidor de desarrollo automticamente detecta los cambios en tu cdigo de Python y recarga de ser necesario, as no tienes que reiniciar el servidor al hacer cambios). El servidor est corriendo en la direccin http://127.0.0.1:8000/, entonces abre tu navegador web y ve a http://127.0.0.1:8000/time/. Deberas ver la salida de tu vista de Django. Enhorabuena! Has creado tu primera pgina Web hecha con Django. Expresiones Regulares: Las Expresiones Regulares (o regexes) son la forma compacta de especicar patrones en un texto. Aunque las URLconfs de Django permiten el uso de regexes arbitrarias para tener un potente sistema de denicin de URLs, probablemente en la prctica no utilices ms que un par de patrones regex. Esta es una pequea seleccin de patrones comunes: Smbolo . (punto) \d [A-Z] [a-z] [A-Za-z] + [^/]+ * {1,3} Coincide con Cualquier carcter Cualquier dgito Cualquier carcter, A-Z (maysculas) Cualquier carcter, a-z (minsculas) Cualquier carcter, a-z (no distingue entre mayscula y minscula) Una o ms ocurrencias de la expresin anterior (ejemplo, \d+ coincidir con uno o ms dgitos) Todos los caracteres excepto la barra. Cero o ms ocurrencias de la expresin anterior (ejemplo, \d* coincidir con cero o ms dgitos) Entre una y tres (inclusive) ocurrencias de la expresin anterior regulares, mira el mdulo
18
apunta al urls.py generado automticamente. Qu conveniente! Cuando llega una peticin digamos, una peticin a la URL /time/ Django carga la URLconf apuntada por la variable ROOT_URLCONF. Luego comprueba cada uno de los patrones de URL en la URLconf en orden, comparando la URL solicitada con un patrn a la vez, hasta que encuentra uno que coincida. Cuando encuentra uno que coincide, llama a la funcin de vista asociada con ese patrn, pasando un objeto HttpRequest como primer parmetro de la funcin. (Veremos ms de HttpRequest luego). La funcin de vista es responsable de retornar un objeto HttpResponse. Conoces ahora lo bsico sobre cmo hacer pginas Web con Django. Es muy sencillo, realmente slo tenemos que escribir funciones de vista y relacionarlas con URLs mediante URLconfs. Podras pensar que es lento enlazar las URL con funciones usando una serie de expresiones regulares, pero te sorprenders.
19
Figura 3-1: El ujo completo de un peticin y una respuesta Django. Cuando llega una peticin HTTP desde el navegador, un manejador especco a cada servidor construye la HttpRequest, para pasarla a los componentes y maneja el ujo del procesamiento de la respuesta. El manejador luego llama a cualquier middleware de Peticin o Vista disponible. Estos tipos de middleware son tiles para *augmenting* los objetos HttpRequest as como tambin para proveer manejo especial a determinados tipos de peticiones. En el caso de que alguno de los mismos retornara un HttpResponse la vista no es invocada. Hasta a los mejores programadores se le escapan errores (bugs), pero el middleware de excepcin ayuda a aplastarlos. Si una funcin de vista lanza una excepcin, el control pasa al middleware de Excepcin. Si este middleware no retorna un HttpResponse, la excepcin se vuelve a lanzar. Sin embargo, no todo est perdido. Django incluye vistas por omisin para respuestas amigables a errores 404 y 500.
20
Finalmente, el middleware de respuesta es bueno para el procesamiento posterior a un HttpResponse justo antes de que se enve al navegador o haciendo una limpieza de recursos especcos a una peticin.
21
Figura 3-2. Pgina 404 de Django La utilidad de esta pgina va ms all del mensaje bsico de error 404; nos dice tambin, qu URLconf utiliz Django y todos los patrones de esa URLconf. Con esa informacin, tendramos que ser capaces de establecer porqu la URL solicitada lanz un error 404. Naturalmente, esta es informacin importante slo destinada a ti, el administrador Web. Si esto fuera un sitio en produccin alojado en Internet, no quisiramos mostrar esta informacin al pblico. Por esta razn, la pgina Page not found es slo mostrada si nuestro proyecto en Django est en modo de depuracin (debug mode). Explicaremos cmo desactivar este modo ms adelante. Por ahora, slo diremos que todos los proyectos estn en modo de depuracin cuando los creamos, y si el proyecto no lo estuviese, se retornara una respuesta diferente.
22
( r ^time/plus/4/$ , four_hours_ahead), )
Claramente, esta lnea de pensamiento es incorrecta. No slo porque producir redundancia entre las funciones de vista, sino tambin la aplicacin estar limitada a admitir slo el rango horario denido uno, dos, tres o cuatro horas. Si, de repente, quisiramos crear una pgina que mostrara la hora cinco horas adelantada, tendramos que crear una vista distinta y una lnea URLconf, perpetuando la duplicacin y la demencia. Aqu necesitamos algo de abstraccin.
Este patrn coincidir con cualquier URL que sea como /time/plus/2/, /time/plus/25/, o tambin /time/plus/100000000000/. Ahora que lo pienso, podemos limitar el lapso mximo de horas en 99. Eso signica que queremos tener nmeros de uno o dos dgitos en la sintaxis de las expresiones regulares, con lo que nos quedara as \d{1,2}:
( r ^time/plus/ \ d{1,2}/$ , hours_ahead),
Nota: Cuando construimos aplicaciones Web, siempre es importante considerar el caso ms descabellado posible de entrada, y decidir si la aplicacin admitir o no esa entrada. Aqu hemos limitado a los exagerados reconociendo lapsos de hasta 99 horas. Y, por cierto, Los Limitadores exagerados, aunque largo, sera un nombre fantstico para una banda musical. Ahora designaremos el comodn para la URL, necesitamos una forma de pasar esa informacin a la funcin de vista, as podremos usar una sola funcin de vista para cualquier adelanto de hora. Lo haremos colocando parntesis alrededor de los datos en el patrn URL que querramos guardar. En el
23
caso del ejemplo, queremos guardar cualquier nmero que se anotar en la URL, entonces pongamos parntesis alrededor de \d{1,2}:
( r ^time/plus/( \ d{1,2})/$ , hours_ahead),
Si ests familiarizado con las expresiones regulares, te sentirs como en casa aqu; estamos usando parntesis para capturar los datos del texto que coincide. La URLconf nal, incluyendo la vista anterior current_datetime, nos quedar algo as:
from django.conf.urls.defaults import * from mysite.views import current_datetime, hours_ahead urlpatterns = patterns( , ( r ^time/$ , current_datetime), ( r ^time/plus/( \ d{1,2})/$ , hours_ahead), )
Con cuidado, vamos a escribir la vista hours_ahead. Orden para programar En este ejemplo, primero escribimos el patrn URL y en segundo lugar la vista, pero en el ejemplo anterior, escribimos la vista primero y luego el patrn de URL. Qu tcnica es mejor? Bien, cada programador es diferente. Si eres del tipo de programadores que piensan globalmente, puede que tenga ms sentido que escribas todos los patrones de URL para la aplicacin al mismo tiempo, al inicio del proyecto, y despus el cdigo de las funciones de vista. Esto tiene la ventaja de darnos una lista de objetivos clara, y es esencial denir los parmetros requeridos por las funciones de vista que necesitaremos desarrollar. Si eres del tipo de programadores que les gusta ir de abajo hacia arriba, tal vez preeras escribir las funciones de vista primero, y luego asociarlas a URLs. Esto tambin est bien. Al nal, todo se reduce a elegir qu tcnica se amolda ms a tu cerebro. Ambos enfoques son vlidos. hours_ahead es muy similar a current_datetime, vista que escribimos antes, slo que con una diferencia: tomar un argumento extra, el nmero de horas que debemos adelantar. Agrega al archivo views.py lo siguiente:
import django.http.HttpResponse import datetime def hours_ahead(request, offset): offset = int(offset) dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = " <html><body>In %s hour(s), it will be %s .</body></html> " % (offset, dt) return HttpResponse(html)
Repasemos el cdigo anterior lnea a lnea: Tal como hicimos en la vista current_datetime, django.http.HttpResponse y el mdulo datetime. importamos la clase
La funcin de vista hours_ahead, toma dos parmetros: request y offset. request es un objeto HttpRequest, al igual que en current_datetime. Lo diremos nuevamente: cada vista siempre toma un objeto HttpRequest como primer parmetro. offset es la cadena de caracteres capturada por los parntesis en el patrn URL. Por ejemplo, si la peticin URL fuera /time/plus/3/, entonces el offset debera ser la cadena de caracteres 3. Si la peticin URL fuera /time/plus/21/, entonces el offset debera ser la cadena de caracteres 21. Notar que la cadena de caracteres capturada siempre es una cadena de caracteres, no un entero, incluso si se compone slo de dgitos, como en el caso 21. 24 Captulo 4. Captulo 3: Los principios de las pginas Web dinmicas
Decidimos llamar a la variable offset, pero puedes asignarle el nombre que quieras, siempre que sea un identicador vlido para Python. El nombre de la variable no importa; todo lo que importa es lo que contiene el segundo parmetro de la funcin (luego de request). Es posible tambin usar una palabra clave, en lugar de posicin, como argumentos en la URLconf. Eso lo veremos en detalle en el Captulo 8. Lo primero que hacemos en la funcin es llamar a int() sobre offset. Esto convierte el valor de la cadena de caracteres a entero. Tener en cuenta que Python lanzar una excepcin ValueError si se llama a la funcin int() con un valor que no puede convertirse a un entero, como lo sera la cadena de caracteres foo. Sin embargo, en este ejemplo no debemos preocuparnos de atrapar la excepcin, porque tenemos la certeza que la variable offset ser una cadena de caracteres conformada slo por dgitos. Sabemos esto, por el patrn URL de la expresin regular en el URLconf (\d{1,2}) captura slo dgitos. Esto ilustra otra ventaja de tener un URLconf: nos provee un primer nivel de validacin de entrada. La siguiente lnea de la funcin muestra la razn por la que se llam a la funcin int() con offset. En esta lnea, calculamos la hora actual ms las hora que tiene offset, almacenando el resultado en la variable dt. La funcin datetime.timedelta requiere que el parmetro hours sea un entero. A continuacin, construimos la salida HTML de esta funcin de vista, tal como lo hicimos en la vista current_datetime. Una pequea diferencia en esta lnea, es que usamos el formato de cadenas de Python con dos valores, no slo uno. Por lo tanto, hay dos smbolos %s en la cadena de caracteres y la tupla de valores a insertar sera: (offset, dt). Finalmente, retornamos el HttpResponse del HTML de nuevo, tal como hicimos en la vista current_datetime. Con esta funcin de vista y la URLconf escrita, ejecuta el servidor de desarrollo de Django (si no est corriendo), y visita http://127.0.0.1:8000/time/plus/3/ para vericar que lo que hicimos funciona. Luego prueba http://127.0.0.1:8000/time/plus/5/. Para terminar visita http://127.0.0.1:8000/time/plus/100/ para vericar que el patrn en la URLconf slo acepta nmero de uno o dos dgitos, Django debera mostrar un error en este caso como Page not found, tal como vimos en la seccin Errores 404 anteriormente. La URL http://127.0.0.1:8000/time/plus/ (sin horas designadas) debera tambin mostrar un error 404. Si ests siguiendo el libro y programando al mismo tiempo, notars que el archivo views.py ahora contiene dos vistas. (Omitimos la vista current_datetime del ejemplo anterior slo por claridad). Ponindolas juntas, veramos algo similar a esto:
from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() html = " <html><body>It is now return HttpResponse(html)
def hours_ahead(request, offset): offset = int(offset) dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = " <html><body>In %s hour(s), it will be %s .</body></html> " % (offset, dt) return HttpResponse(html)
25
def hours_ahead(request, offset): #offset = int(offset) dt = datetime.datetime.now() + datetime.timedelta(hours=offset) html = " <html><body>In %s hour(s), it will be %s .</body></html> " % (offset, dt) return HttpResponse(html)
Ejecuta el servidor de desarrollo y navega a /time/plus/3/. Vers una pgina de error con mucha informacin signicativa, incluyendo el mensaje TypeError mostrado en la parte superior de la pgina: nsupported type for timedelta hours component: str". Qu ha ocurrido? Bueno, la funcin datetime.timedelta espera que el parmetro hours sea un entero, y hemos comentado la lnea de cdigo que realiza la conversin del offset a entero. Eso causa que datetime.timedelta lance un TypeError. Es el tpico pequeo bug que todo programador comete en algn momento. El punto de este ejemplo fue demostrar la pgina de error de Django. Dediquemos un momento a explorar esta pgina y descubrir las distintas piezas de informacin que nos brinda. Aqu comentamos algunas cosas a destacar: En la parte superior de la pgina se muestra la informacin clave de la excepcin: el tipo y cualquier parmetro de la excepcin (el mensaje nsupported type" en este caso), el archivo en el cul la excepcin fue lanzada, y el nmero de lnea que contiene el error. Abajo de la informacin clave de la excepcin, la pgina muestra el *traceback* de Python para dicha excepcin. Este es el *traceback* estndar que se obtiene en el interprete de Python, slo que ms interactivo. Por cada marco de la pila, Django muestra el nombre del archivo, el nombre de la funcin/mtodo, el nmero de lnea, y el cdigo fuente de esa lnea. Haz click en la lnea de cdigo (en gris oscuro) para ver varias lneas anteriores y posteriores a la lnea errnea, lo que nos brinda un poco de contexto. Haz click en Locals vars debajo de cualquier marco de la pila para ver la tabla de todas las variables locales y sus valores, en ese marco y en la posicin exacta de cdigo en el cual fue lanzada la excepcin. Esta informacin de depuracin es invaluable. Nota el texto Switch to copy-and-paste view debajo de la cabecera Traceback. Haz click en esas palabras, y el *traceback* cambiar a una versin que te permitir fcilmente copiar y pegar. Usando esto para cuando necesitemos compartir el traceback de la excepcin con otros para obtener soporte tcnico como los amables colegas que encontraremos en el canal de IRC o la lista de correo de Django. A continuacin, la seccin Request information incluye una gran cantidad de informacin sobre la peticin Web que provoc el error: informacin GET y POST, valores de las cookies y meta informacin como las cabeceras CGI. El Apndice H es una completa referencia sobre toda la informacin que contienen los objetos peticiones. Ms abajo, la seccin Settings lista la conguracin de la instalacin de Django en particular. El Apndice E, cubre en detalle los ajustes de conguracin disponibles. Por ahora, slo mira los ajustes para tener una idea de la informacin disponible. La pgina de error de Django es capaz de mostrar ms informacin en ciertos casos especiales, como por ejemplo, en el caso de error de sintaxis en las plantillas. Lo abordaremos ms tarde, cuando discutamos el sistema de plantillas de Django. Por ahora, quita el comentario en la lnea offset = int(offset) para que la funcin de vista funcione normalmente de nuevo. Eres el tipo de programador al que le gusta depurar con la ayuda de sentencias print cuidadosamente colocadas? Puedes usar la pgina de error de Django para hacer eso sin la sentencia print. En cualquier punto de la vista, temporalmente podemos insertar un assert False para provocar una pgina de error. Luego, podremos ver las variables locales y el estado del programa. (Hay maneras ms avanzadas de depurar las vista en Django, lo explicaremos ms adelante, pero esto es la forma ms rpida y fcil). Finalmente, es obvio que la mayor parte de la informacin es delicada expone las entraas del cdigo fuente de Python, como tambin de la conguracin de Django y sera una estupidez mostrarla al 26 Captulo 4. Captulo 3: Los principios de las pginas Web dinmicas
pblico en Internet. Una persona con malas intenciones podra usar esto para intentar aplicar ingeniera inversa en la aplicacin Web y hacer cosas maliciosas. Por esta razn, la pgina de error es mostrada slo cuando el proyecto est en modo depuracin. Explicaremos cmo desactivar este modo ms adelante. Por ahora, hay que tener en claro que todos los proyectos de Django estn en modo depuracin automticamente cuando son creados. (Suena familiar? Los errores Page not found, descriptos en la seccin Errores 404, trabajan de manera similar.)
4.8 Qu sigue?
Hasta ahora hemos producido las vistas mediante cdigo HTML dentro del cdigo Python. Desafortunadamente, esto es casi siempre es una mala idea. Pero por suerte, con Django podemos hacer esto con un potente motor de plantillas que nos permite separar el diseo de las pginas del cdigo fuente subyacente. Nos sumergiremos en el motor de plantillas de Django en el prximo capitulo
4.8. Qu sigue?
27
28
CAPTULO 5
En el captulo anterior, quizs notaste algo extrao en cmo retornbamos el texto en nuestras vistas de ejemplos. A saber, el HTML fue codicado 1 directamente en nuestro cdigo Python. Este convenio conduce a problemas severos: Cualquier cambio en el diseo de la pgina requiere un cambio en el cdigo de Python. El diseo de un sitio tiende a cambiar ms frecuentemente que el cdigo de Python subyacente, por lo que sera conveniente si el diseo podra ser cambiado sin la necesidad de modicar el cdigo Python. Escribir cdigo Python y disear HTML son dos disciplinas diferentes, y la mayora de los entornos de desarrollo web profesional dividen estas responsabilidades entre personas separadas (o incluso en departamento separados). Diseadores y programadores HTML/CSS no deberan tener que editar cdigo Python para conseguir hacer su trabajo; ellos deberan tratar con HTML. Asimismo, esto es ms eciente si los programadores pueden trabajar sobre el cdigo Python y los diseadores sobre las plantillas al mismo tiempo, ms bien que una persona espere por otra a que termine de editar un solo archivo que contiene ambos: Python y HTML. Por esas razones, es mucho ms limpio y mantenible separar el diseo de la pgina del cdigo Python en s mismo. Podemos hacer esto con el sistema de plantillas de Django, el cual trataremos en este captulo.
29
<p>Thanks for placing an order from {{ company }}. Its scheduled to ship on {{ ship_date|date:"F j, Y" }}.</p> <p>Here are the items youve ordered:</p> <ul> {% for item in item_list %} <li>{{ item }}</li> {% endfor %} </ul> {% if ordered_warranty %} <p>Your warranty information will be included in the packaging.</p> {% endif %} <p>Sincerely,<br />{{ company }}</p> </body> </html>
Esta plantilla es un HTML bsico con algunas variables y etiquetas de plantillas agregadas. Vamos paso a paso a travs de sta: Cualquier texto encerrado por un par de llaves (por ej. {{ person_name }}) es una variable. Esto signica insertar el valor de la variable a la que se dio ese nombre. Cmo especicamos el valor de las variables?. Vamos a llegar a eso en un momento. Cualquier texto que est rodeado por llaves y signos de porcentaje (por ej. { % if ordered_warranty %}) es una etiqueta de plantilla. La denicin de etiqueta es bastante amplia: una etiqueta slo le indica al sistema de plantilla haz algo. Este ejemplo de plantilla contiene dos etiquetas: la etiqueta { % for item in item_list %} (una etiqueta for) y la etiqueta { % if ordered_warranty %} (una etiqueta if). Una etiqueta for acta como un simple constructor de bucle, dejndote recorrer a travs de cada uno de los items de una secuencia. Una etiqueta if, como quizs esperabas, acta como una clusula lgica if. En este caso en particular, la etiqueta comprueba si el valor de la variable ordered_warranty se evala como True. Si lo hace, el sistema de plantillas mostrar todo lo que hay entre { % if ordered_warranty %} y { % endif %}. Si no, el sistema de plantillas no mostrar esto. El sistema de plantillas tambin admite { % else %} y otras varias clusulas lgicas. Finalmente, el segundo prrafo de esta plantilla tiene un ejemplo de un ltro, con el cual puedes alterar la exposicin de una variable. En este ejemplo, {{ ship_date|date:"F j, Y"}}, estamos pasando la variable ship_date por el ltro date, tomando el ltro date el argumento "F j, Y". El ltro date formatea fechas en el formato dado, especicado por ese argumento. Los ltros se encadenan mediante el uso de un caracter pipe (|), como una referencia a las tuberas de Unix. Cada plantilla de Django tiene acceso a varias etiquetas y ltros incorporados, algunos de los cuales sern tratados en la seccin que sigue. El Apndice F contiene la lista completa de etiquetas y ltros, y es una buena idea familiarizarse con esta lista, de modo que sepas qu es posible. Tambin es posible crear tus propios ltros y etiquetas, los cuales cubriremos en el Captulo10.
2. Llama al mtodo render() del objeto Template con un conjunto de variables (o sea, el contexto). Este retorna una plantilla totalmente renderizada como una cadena de caracteres, con todas las variables y etiquetas de bloques evaluadas de acuerdo al contexto. Las siguientes secciones describen cada uno de los pasos con mayor detalle.
Esos tres puntos al comienzo de una lnea adicional son insertados por el shell de Python no son parte de nuestra entrada. Los incluimos aqu para ser eles a la salida real del intrprete. Si ests copiando nuestros ejemplos para seguirlos, no copies esos puntos. Desde dentro del directorio del proyecto creado por django-admin.py startproject (como se expuso en el Captulo 2), escribe python manage.py shell para comenzar el intrprete interactivo. Aqu hay un ensayo bsico:
>>> from django.template import Template >>> t = Template( " My name is {{ name }}. " ) >>> print t
Ese 0xb7d5f24c ser distinto cada vez, y realmente no importa; es la forma simple en que Python identica un objeto de Template. Variables de conguracin de Django Cuando usas Django, necesitas indicarle qu valores usar para sus variables de conguracin. Interactivamente, suele usarse python manage.py shell, pero tienes otras opciones descriptas en el Apndice E. Cuando creas un objeto Template, el sistema de plantillas compila el cdigo en crudo a uno interno, de forma optimizada, listo para renderizar. Pero si tu cdigo de plantilla incluye errores de sintaxis, la llamada a Template() causar una excepcin TemplateSyntaxError:
31
El sistema lanza una excepcin TemplateSyntaxError por alguno de los siguientes casos: Bloques de etiquetas invlidos Argumentos invlidos para una etiqueta vlida Filtros invlidos Argumentos invlidos para ltros vlidos Sintaxis invlida de plantilla Etiquetas de bloque sin cerrar (para etiquetas de bloque que requieran la etiqueta de cierre)
Diccionarios y Contextos Un diccionario de Python es un mapeo entre llaves conocidas y valores de variables. Un Context es similar a un diccionario, pero un Context provee funcionalidad adicional, como se cubre en el Captulo 10. Los nombres de las variables deben comenzar con una letra (A-Z o a-z) y pueden contener dgitos, guiones bajos y puntos. (Los puntos son un caso especial al que llegaremos en un momento). Los nombres de variables son sensible a maysculas-minsculas. Este es un ejemplo de compilacin y renderizacin de una plantilla, usando la plantilla de muestra del comienzo de este captulo:
>>> from django.template import Template, Context >>> raw_template = """ <p>Dear {{ person_name }},</p> ... ... <p>Thanks for ordering {{ product }} from {{ company }}. It s scheduled ... to ship on {{ ship_date|date: " F j, Y " ... ... { % i f ordered_warranty % } ... ... ... >>> >>> >>> { % e ndif % } }}.</p>
<p>Sincerely,<br />{{ company }}</p> """ t = Template(raw_template) import datetime c = Context({ person_name : John Smith ,
32
product : Super Lawn Mower , company : Outdoor Equipment , ship_date : datetime.date(2009, 4, 2),
... ordered_warranty : True}) >>> t.render(c) "<p>Dear John Smith,</p>\n\n<p>Thanks for ordering Super Lawn Mower from Outdoor Equipment. Its scheduled \nto ship on April 2, 2009.</p>\n\n\n <p>Your warranty information will be included in the packaging.</p>\n\n\n <p>Sincerely,<br />Outdoor Equipment</p>"
Vamos paso a paso por este cdigo, de a una sentencia a la vez: Primero, importamos la clase Template y Context, ambas se encuentran en el mdulo django.template. Guardamos en texto crudo de nuestra plantilla en la variable raw_template. Note que usamos triple comillas para delimitar la cadena de caracteres, debido a que abarca varias lneas; en el cdigo Python, las cadenas de caracteres delimitadas con una sola comilla indican que no puede abarcar varias lneas. Luego, creamos un objeto plantilla, t, pasndole raw_template al constructor de la clase Template. Importamos el mdulo datetime desde la biblioteca estndar de Python, porque lo vamos a necesitar en la prxima sentencia. Entonces, creamos un objeto Context , c. El constructor de Context toma un diccionario de Python, el cual mapea nombres de variables con valores. Aqu, por ejemplo, especicamos que person_name es John Smith, product es Super Lawn Mower, y as sucesivamente. Finalmente, llamamos al mtodo render() sobre nuestro objeto de plantilla, pasando a ste el contexto. Este retorna la plantilla renderizada esto es, reemplaza las variables de la plantilla con los valores reales de las variables, y ejecuta cualquier bloque de etiquetas. Nota que el prrafo de garanta fue mostrado porque la variable ordered_warranty se evala como True. Tambin nota que la fecha April 2, 2009, es mostrada acorde al formato de cadena de caracteres F j, Y. (Explicaremos los formatos de cadenas de caracteres para el ltro date a la brevedad). Si eres nuevo en Python, quizs te preguntes por qu la salida incluye los caracteres de nueva lnea (\n) en vez de mostrar los saltos de lnea. Esto sucede porque es una sutileza del intrprete interactivo de Python: la llamada a t.render(c) retorna una cadena de caracteres, y el intrprete interactivo, por omisin, muestra una representacin de sta, en vez de imprimir el valor de la cadena. Si quieres ver la cadena de caracteres con los saltos de lneas como verdaderos saltos de lneas en vez de caracteres \n, usa la sentencia print: print t.render(c). Estos son los fundamentos del uso del sistema de plantillas de Django: slo escribe una plantilla, crea un objeto Template, crea un Context, y llama al mtodo render().
33
Cuando ests usando la misma plantilla fuente para renderizar mltiples contextos como este, es ms eciente crear el objeto Template una sola vez y luego llamar a render() sobre ste muchas veces:
# Bad for name in ( John , Julie , Pat ): t = Template( Hello, {{ name }} ) print t.render(Context({ name : name})) # Good t = Template( Hello, {{ name }} ) for name in ( John , Julie , Pat ): print t.render(Context({ name : name}))
El anlisis sintctico de las plantillas de Django es bastante rpido. Detrs de escena, la mayora de los analizadores pasan con una simple llamada a una expresin regular corta. Esto es un claro contraste con el motor de plantillas de XML, que incurre en la excesiva actividad de un analizador XML, y tiende a ser rdenes de magnitud ms lento que el motor de renderizado de Django.
De forma similar, los puntos te permiten acceder a los atributos de los objetos. Por ejemplo, un objeto de Python datetime.date tiene los atributos year, month y day, y puedes usar el punto para acceder a ellos en las plantillas de Django:
>>> from django.template import Template, Context >>> import datetime >>> d = datetime.date(1993, 5, 2) >>> d.year 1993 >>> d.month 5 >>> d.day 2 >>> t = Template( The month is {{ date.month }} and the year is {{ date.year }}. ) >>> c = Context({ date : d}) >>> t.render(c) The month is 5 and the year is 1993.
34
>>> t = Template( Hello, {{ person.first_name }} {{ person.last_name }}. ) >>> c = Context({ person : Person( John , Smith )}) >>> t.render(c) Hello, John Smith.
Los puntos tambin son utilizados para llamar a mtodos sobre los objetos. Por ejemplo, cada cadena de caracteres de Python tiene el mtodos upper() y isdigit(), y puedes llamar a estos en las plantillas de Django usando la misma sintaxis de punto:
>>> from django.template import Template, Context >>> t = Template( {{ var }} -- {{ var.upper }} -- {{ var.isdigit }} ) >>> t.render(Context({ var : hello })) hello -- HELLO -- False >>> t.render(Context({ var : 123 })) 123 -- 123 -- True
Nota que no tienes que incluir los parntesis en las llamadas a los mtodos. Adems, tampoco es posible pasar argumentos a los mtodos; slo puedes llamar los mtodos que no requieran argumentos. (Explicaremos esta losofa luego en este captulo). Finalmente, los puntos tambin son usados para acceder a los ndices de las listas, por ejemplo:
>>> from django.template import Template, Context >>> t = Template( Item 2 is {{ items.2 }}. ) >>> c = Context({ items : [ apples , bananas , carrots ]}) >>> t.render(c) Item 2 is carrots.
Los ndices negativos de las listas no estn permitidos. Por ejemplo, la variable {{ items.-1 }} causar una TemplateSyntaxError. Listas de Python Las listas de Python comienzan en cero, entonces el primer elemento es el 0, el segundo es el 1 y as sucesivamente. La bsqueda del punto puede resumirse como esto: cuando un sistema de plantillas encuentra un punto en una variable, ste intenta la siguiente bsqueda, en este orden: Diccionario (por ej. foo["bar"]) Atributo (por ej. foo.bar) Llamada de mtodo (por ej. foo.bar()) ndice de lista (por ej. foo[bar]) El sistema utiliza el primer tipo de bsqueda que funcione. Es la lgica de cortocircuito. Los puntos pueden ser anidados a mltiples niveles de profundidad. El siguiente ejemplo usa {{ person.name.upper }}, el que se traduce en una bsqueda de diccionario (person[name]) y luego en una llamada a un mtodo (upper()):
>>> from django.template import Template, Context >>> person = { name : Sally , age : 43 } >>> t = Template( {{ person.name.upper }} is {{ person.age }} years old. ) >>> c = Context({ person : person}) >>> t.render(c) SALLY is 43 years old.
35
Comportamiento de la llamada a los mtodos La llamada a los mtodos es ligeramente ms compleja que los otros tipos de bsqueda. Aqu hay algunas cosas a tener en cuenta: Si, durante la bsqueda de mtodo, un mtodo provoca una excepcin, la excepcin ser propagada, a menos que la excepcin tenga un atributo silent_variable_failure cuyo valor sea True. Si la excepcin tiene el atributo silent_variable_failure, la variable ser renderizada como un string vaco, por ejemplo:
>>> t = Template( " My name is {{ person.first_name }}. " ) >>> class PersonClass3: ... def first_name(self): ... raise AssertionError, " foo " >>> p = PersonClass3() >>> t.render(Context({ " person " : p})) Traceback (most recent call last): ... AssertionError: foo >>> ... >>> ... ... >>> >>> "My class SilentAssertionError(AssertionError): silent_variable_failure = True class PersonClass4: def first_name(self): raise SilentAssertionError p = PersonClass4() t.render(Context({ " person " : p})) name is ."
La llamada a un mtodo funcionar slo si el mtodo no requiere argumentos. En otro caso, el sistema pasar a la siguiente bsqueda de tipo (ndice de lista). Evidentemente, algunos mtodos tienen efectos secundarios, por lo que sera absurdo, en el mejor de los casos, y posiblemente un agujero de seguridad, permitir que el sistema de plantillas tenga acceso a ellos. Digamos, por ejemplo, tienes objeto BankAccount que tiene un mtodo delete(). Una plantilla no debera permitir incluir algo como {{ account.delete }}. Para prevenir esto, asigna el atributo alters_data de la funcin en el mtodo:
def delete(self): # Delete the account delete.alters_data = True
El sistema de plantillas no debera ejecutar cualquier mtodo marcado de este modo. En otras palabras, si una plantilla incluye {{ account.delete }}, esta etiqueta no ejecutar el mtodo delete(). Este fallar silenciosamente. Cmo se manejan las variables invlidas? Por omisin, si una variable no existe, el sistema de plantillas renderiza este como un string vaco, fallando silenciosamente, por ejemplo:
>>> from django.template >>> t = Template( Your >>> t.render(Context()) Your name is . >>> t.render(Context({ Your name is . >>> t.render(Context({ Your name is . import Template, Context name is {{ name }}. )
36
El sistema falla silenciosamente en vez de levantar una excepcin porque intenta ser exible a los errores humanos. En este caso, todas las bsquedas fallan porque los nombres de las variables, o su capitalizacin es incorrecta. En el mundo real, es inaceptable para un sitio web ser inaccesible debido a un error de sintaxis tan pequeo. Ten en cuenta que es posible cambiar el comportamiento por omisin de Django en este sentido, ajustando la conguracin de Django. Discutiremos esto ms adelante en el Captulo 10.
5.3.1 Etiquetas
if/else La etiqueta { % if %} evala una variable, y si esta es true (esto es, existe, no est vaca y no es un valor Boolean falso), el sistema mostrar todo lo que hay entre { % if %} y { % endif %}, por ejemplo:
{% if today_is_weekend %} <p>Welcome to the weekend!</p> {% endif %}
Las verdades de Python En Python, la lista vaca ([]), la tupla vaca (()), el diccionario vaco ({}), la cadena vaca (), el cero (0), y el objeto especial None son False en un contexto booleano. Todo lo dems es True.
37
La etiqueta { % if %} acepta and, or, o not para testear mltiples variables, o para negarlas. Por ejemplo:
{% if athlete_list and coach_list %} Both athletes and coaches are available. {% endif %} {% if not athlete_list %} There are no athletes. {% endif %} {% if athlete_list or coach_list %} There are some athletes or some coaches. {% endif %} {% if not athlete_list or coach_list %} There are no athletes or there are some coaches. (OK, so writing English translations of Boolean logic sounds stupid; its not our fault.) {% endif %} {% if athlete_list and not coach_list %} There are some athletes and absolutely no coaches. {% endif %}
Las etiquetas { % if %} no permiten las clusulas and y or en la misma etiqueta, porque el orden de evaluacin lgico puede ser ambiguo. Por ejemplo, esto es invlido: { % if athlete_list and coach_list or cheerleader_list %} No se admite el uso de parntesis para controlar el orden de las operaciones. Si necesitas parntesis, considera efectuar la lgica en el cdigo de la vista para simplicar las plantillas. An as, si necesitas combinar and y or para hacer lgica avanzada, usa etiquetas { % if %} anidadas, por ejemplo:
{% if athlete_list %} {% if coach_list or cheerleader_list %} We have athletes, and either coaches or cheerleaders! {% endif %} {% endif %}
Usar varias veces el mismo operador lgico estn bien, pero no puedes combinar diferentes operadores. Por ejemplo, esto es vlido: { % if athlete_list or coach_list or parent_list or teacher_list %} Ah no hay una etiqueta { % elif %}. Usa etiquetas { % if %} anidadas para conseguir alguna cosa:
{% if athlete_list %} <p>Here are the athletes: {{ athlete_list }}.</p> {% else %} <p>No athletes are available.</p> {% if coach_list %} <p>Here are the coaches: {{ coach_list }}.</p> {% endif %} {% endif %}
Asegrate de cerrar cada { % if %} con un { % endif %}. En otro caso, Django levantar la excepcin TemplateSyntaxError. for La etiqueta { % for %} permite iterar sobre cada uno de los elementos de una secuencia. Como en la sentencia for de Python, la sintaxis es for X in Y, dnde Y es la secuencia sobre la que se hace el
38
bucle y X es el nombre de la variable que se usar para cada uno de los ciclos del bucle. Cada vez que atravesamos el bucle, el sistema de plantillas renderizar todo entre { % for %} y { % endfor %}. Por ejemplo, puedes usar lo siguiente para mostrar una lista de atletas tomadas de la variable athlete_list:
<ul> {% for athlete in athlete_list %} <li>{{ athlete.name }}</li> {% endfor %} </ul>
No se admite la ruptura de un bucle antes de que termine. Si quieres conseguir esto, cambia la variable sobre la que ests iterando para que incluya slo los valores sobre los cuales quieres iterar. De manera similar, no hay apoyo para la sentencia continue que se encargue de retornar inmediatamente al inicio del bucle. (Ve a la seccin Filosofa y limitaciones luego en este captulo para comprender el razonamiento detrs de este decisin de diseo.) La etiqueta { % for %} asigna la variable forloop mgica a la plantilla con el bucle. Esta variable tiene algunos atributos que toman informacin acerca del progreso del bucle: forloop.counter es siempre asignada a un nmero entero representando el nmero de veces que se ha entrado en el bucle. Esta es indexada a partir de 1, por lo que la primera vez que se ingresa al bucle, forloop.counter ser 1. Aqu un ejemplo:
{ % for item in todo_list %} <p>{{ forloop.counter }}: {{ item }}</p> { % endfor %}
forloop.counter0 es como forloop.counter, excepto que esta es indexada a partir de cero. Contendr el valor 0 la primera vez que se atraviese el bucle. forloop.revcounter es siempre asignada a un entero que representa el nmero de iteraciones que faltan para terminar el bucle. La primera vez que se ejecuta el bucle forloop.revcounter ser igual al nmero de elementos que hay en la secuencia. La ltima vez que se atraviese el bucle, a forloop.revcounter se la asignar el valor 1. forloop.revcounter0 es como forloop.revcounter, a excepcin de que est indexada a partir de cero. La primera vez que se atraviesa el bucle, forloop.revcounter0 es asignada al nmero de elementos que hay en la secuencia menos 1. La ltima vez que se atraviese el bucle, el valor de esta ser 0. forloop.first es un valor booleano asignado a True si es la primera vez que se pasa por el bucle. Esto es conveniente para ocasiones especiales:
{ % for object in objects %} { % if forloop.first %}<li class="first">{ % else %}<li>{ % endif %} {{ object }}
39
</li> { % endfor %}
forloop.last es un valor booleano asignado a True si es la ltima pasada por el bucle. Un uso comn es para esto es poner un carcter pipe entre una lista de enlaces:
{ % for link in links %}{{ link }} { % if not forloop.last %} | { % endif %}{ % endfor %}
forloop.parentloop esta es una referencia al objeto padre de forloop, en el caso de bucles anidados. Aqu un ejemplo:
{ % for country in countries %} <table> { % for city in country.city_list %} <tr> <td>Country #{{ forloop.parentloop.counter }}</td> <td>City #{{ forloop.counter }}</td> <td>{{ city }}</td> </tr> { % endfor %} </table> { % endfor %}
La variable mgica forloop est slo disponible dentro de bucles. Despus de que el analizados sintctico encuentra { % endfor %}, forloop desaparece. Nota: Contextos y la variable forloop Dentro de un bloque { % for %}, las variables existentes se mueven fuera de tal manera de evitar sobreescribir la variable mgica forloop. Django expone ese contexto movido en forloop.parentloop. Generalmente no necesitas preocuparte por esto, si provees una variable a la plantilla llamada forloop (a pesar de que no lo recomendamos), se llamar forloop.parentloop mientras est dentro del bloque { % for %}.
ifequal/ifnotequal El sistema de plantillas de Django a propsito no es un lenguaje de programacin completo y por lo tanto no permite ejecutar sentencias arbitrarias de Python. (Ms sobre esta idea en la seccin Filosofa y limitaciones). Sin embargo, es bastante comn que una plantilla requiera comparar dos valores y mostrar algo si ellos son iguales Django provee la etiqueta { % ifequal %} para este propsito. La etiqueta { % ifequal %} compara dos valores y muestra todo lo que se encuentra entre { % ifequal %} y { % endifequal %} si el valor es igual. Este ejemplo compara las variables user y currentuser de la plantilla:
{% ifequal user currentuser %} <h1>Welcome!</h1> {% endifequal %}
Los argumentos pueden ser strings hard-codeados, con simples o dobles comillas, lo siguiente es vlido:
{% ifequal section sitenews %} <h1>Site News</h1> {% endifequal %} {% ifequal section "community" %}
40
<h1>Community</h1> {% endifequal %}
Slo las variables de la plantilla, string, enteros y nmeros decimales son permitidos como argumentos para { % ifequal %}. Estos son ejemplos vlidos:
{% {% {% {% ifequal ifequal ifequal ifequal variable variable variable variable 1 %} 1.23 %} foo %} "foo" %}
Cualquier otro tipo de variables, tales como diccionarios de Python, listas, o booleanos, no pueden ser comparados en { % ifequal %}. Estos ejemplos son invlidos: { % ifequal variable True %} { % ifequal variable [1, 2, 3] %} { % ifequal variable {key: value} %} Si necesitas comprobar cuando algo es verdadero o falso, usa la etiqueta { % if %} en vez de { % ifequal %}. Comentarios Al igual que en HTML o en un lenguaje de programacin como Python, el lenguaje de plantillas de Django permite comentarios. Para designar un comentario, usa {# #}:
{# This is a comment #}
Este comentario no ser mostrado cuando la plantilla sea renderizada. Un comentario no puede abarcar mltiples lneas. Esta limitacin mejora la performance del analizador sintctico de plantillas. En la siguiente plantilla, la salida del renderizado mostrara exactamente lo mismo que la plantilla (esto es, la etiqueta comentario no ser tomada como comentario):
This is a {# this is not a comment #} test.
5.3.2 Filtros
Como explicamos anteriormente en este captulo, los ltros de plantillas son formas simples de alterar el valor de una variable antes de mostrarla. Los ltros se parecen a esto: {{ name|lower }} Esto muestra el valor de {{ name }} despus de aplicarle el ltro lower, el cual convierte el texto a minscula. Usa un pipe (|) para aplicar el ltro. Los ltros pueden estar en cadena eso es, la salida del uno de los ltros puede ser aplicada al prximo. Aqu un modismo comn para escapar contenido del texto, y entonces convertir los saltos de lneas en etiquetas <p>: {{ my_text|escape|linebreaks }} Algunos ltros toman argumentos. Un ltro con argumento se ve de este modo:
41
{{ bio|truncatewords:30 }} Esto muestra las primeras 30 palabras de la variable bio. Los argumentos de los ltros estn siempre entre comillas dobles. Los siguientes son algunos de los ltros ms importantes; el Apndice F cubre el resto. addslashes: Agrega una con contra-barra antes de cualquier contra-barra, comilla simple o comilla doble. Esto es til si el texto producido est incluido en un string de JavaScript. date: Formatea un objeto date o datetime de acuerdo al formato tomado como parmetro, por ejemplo: {{ pub_date|date:F j, Y }} El formato de los strings est denido en el Apndice F. escape: Escapa ampersands(&), comillas, y corchetes del string tomado. Esto es usado para desinfectar datos suministrados por el usuario y asegurar que los datos son vlidos para XML y XHTML. Especcamente, escape hace estas conversiones: Convierte & en & Convierte < en < Convierte > en > Convierte " (comilla doble) en " Convierte (comilla simple) en ' length: Retorna la longitud del valor. Puedes usar este con una lista o con un string, o con cualquier objeto Python que sepa como determinar su longitud (o sea cualquier objeto que tenga el mtodo __len__()).
42
hacer. Es posible escribir etiquetas personalizadas que hagan cosas arbitrarias, pero las etiquetas de Django intencionalmente no permiten ejecutar cdigo arbitrario de Python. La sintaxis debe ser independiente de HTML/XML. Aunque el sistemas de plantillas de Django es usado principalmente para producir HTML, este pretende ser til para formatos no HTML, como texto plano. Algunos otros lenguajes de plantillas estn basados en XML, poniendo toda la lgica de plantilla con etiquetas XML o atributos, pero Django evita deliberadamente esta limitacin. Requerir un XML vlido para escribir plantillas introduce un mundo de errores humanos y mensajes difcil de entender, y usando un motor de XML para parsear plantillas implica un inaceptable nivel de overhead en el procesamiento de la plantilla. Los diseadores se supone que se sienten ms cmodos con el cdigo HTML. El sistema de plantillas no est diseado para que las plantillas necesariamente sean mostradas de forma agradable en los editores WYSIWYG 2 tales como Dreamweaver. Eso es tambin una limitacin severa y no permitira que la sintaxis sea tan clara como lo es. Django espera las plantillas de los autores para estar cmodo editando HTML directamente. Se supone que los diseadores no son programadores Python. El sistema de plantillas de los autores reconoce que las plantillas de las pginas web son en al mayora de los casos escritos por diseadores, no por programadores, y por esto no debera asumir ningn conocimiento de Python. Sin embargo, el sistema tambin pretende acomodar pequeos grupos en los cuales las plantillas sean creadas por programadores de Python. Esto ofrece otro camino para extender la sintaxis del sistema escribiendo cdigo Python puro. (Ms de esto en el Captulo 10). El objetivo no es inventar un lenguaje de programacin. El objetivo es ofrecer slo la suciente funcionalidad de programacin, tales como ramicacin e iteracin, que son esenciales para hacer presentaciones relacionadas a decisiones. Como resultado de esta losofa, el lenguaje de plantillas de Django tiene las siguientes limitaciones: Una plantilla no puede asignar una variable o cambiar el valor de esta. Esto es posible escribiendo una etiqueta personalizada para cumplir con esta meta (ve el Captulo 10), pero la pila de etiquetas de Django no lo permite. Una plantilla no puede llamar cdigo Python crudo. No hay forma de ingresar en modo Python o usar sentencias puras de Python. De nuevo, esto es posible creando plantillas personalizadas, pero la pila de etiquetas de Django no lo permiten.
Vamos a cambiar esta vista usando el sistema de plantillas de Django. Primero, podemos pensar en algo como esto:
from django.template import Template, Context from django.http import HttpResponse import datetime
2
N. del T.: WYSIWYG: What you see is what you get (Lo que ves es lo que obtienes)
43
def current_datetime(request): now = datetime.datetime.now() t = Template( " <html><body>It is now {{ current_date }}.</body></html> " ) html = t.render(Context({ current_date : now})) return HttpResponse(html)
Seguro, esto usa el sistema de plantillas, pero no soluciona el problema que planteamos en la introduccin de este captulo. A saber, la plantilla sigue estando embebida en el cdigo Python. Vamos a solucionar esto poniendo la plantilla en un archivo separado, que la vista cargar. Puedes primer considerar guardar la plantilla en algn lugar del disco y usar las funcionalidades de Python para abrir y leer el contenido de la plantilla. Esto puede verse as, suponiendo que la plantilla est guardada en /home/djangouser/templates/mytemplate.html:
from django.template import Template, Context from django.http import HttpResponse import datetime def current_datetime(request): now = datetime.datetime.now() # Simple way of using templates from the filesystem. # This doesnt account for missing files! fp = open( /home/djangouser/templates/mytemplate.html ) t = Template(fp.read()) fp.close() html = t.render(Context({ current_date : now})) return HttpResponse(html)
Esta aproximacin, sin embargo, es poco elegante por estas razones: No maneja el caso en que no encuentre el archivo. Si el archivo mytemplate.html no existe o no es accesible para lectura, la llamada a open() levantar la excepcin IOError. Involucra la ruta de tu plantilla. Si vas a usar esta tcnica para cada una de las funciones de las vistas, estars duplicando rutas de plantillas. Sin mencionar que esto implica teclear mucho ms! Incluye una cantidad aburrida de cdigo repetitivo. Tienes mejores cosas para hacer en vez de escribir open(), fp.read() y fp.close() cada vez que cargas una plantilla Para solucionar estos problemas, usamos cargadores de plantillas y directorios de plantillas, los cuales son descriptos en las secciones que siguen.
N. del T.: API: Application Program Interface (Interfaz de programacin de aplicaciones) N. del T.: aka: Also Know As (Tambin conocido como)
44
TIME_ZONE = America/Chicago USE_I18N = True ROOT_URLCONF = mysite.urls stas se explican por s solas; las conguraciones y sus respectivos valores son simples variables de Python. Como el archivo de conguracin es slo un mdulo plano de Python, puedes hacer cosas dinmicas como vericar el valor de una variable antes de congurar otra. (Esto tambin signica que debes evitar errores de sintaxis de Python en los archivos de conguracin). Cubriremos el archivo de conguracin en profundidad en el Apndice E, pero por ahora, veamos la variable de conguracin TEMPLATE_DIRS. Esta variable le indica al mecanismo de carga de plantillas dnde buscar las plantillas. Por omisin, sta es una tupla vaca. Elige un directorio en el que desees guardar tus plantillas y agrega este a TEMPLATE_DIRS, as: TEMPLATE_DIRS = (/home/django/mysite/templates,) Hay algunas cosas para notar: Puedes especicar cualquier directorio que quieras, siempre y cuando la cuenta de usuario en el cual se ejecuta el servidor web tengan acceso al directorio y su contenido. Si no puedes pensar en un lugar apropiado para poner las plantillas, te recomendamos crear un directorio templates dentro del proyecto de Django (esto es, dentro del directorio mysite que creaste en el Captulo 2 , si vienes siguiendo los ejemplos a lo largo del libro). 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 expresin de parntesis. Esto es comn en los usuarios nuevos. Si quieres evitar este error, puedes hacer TEMPLATE_DIRS una lista, en vez de una tupla, porque un solo elemento en una lista no requiere estar seguido de una coma: TEMPLATE_DIRS = [/home/django/mysite/templates] Una tupla es un poco ms correcta semnticamente que una lista (las tuplas no pueden cambiar luego de ser creadas, y nada podra cambiar las conguraciones una vez que fueron ledas), nosotros recomendamos usar tuplas para la variable TEMPLATE_DIRS. Si ests en Windows, incluye tu letra de unidad y usa el estilo de Unix para las barras en vez de barras invertidas, como sigue: TEMPLATE_DIRS = (C:/www/django/templates,) Es ms sencillo usar rutas absolutas (esto es, las rutas de directorios comienzan desde la raz del sistema de archivos). Si quieres sen un poco ms exible e independiente, tambin, puedes tomar el hecho de que el archivo de conguracin de Django es slo cdigo de Python y construir la variable TEMPLATE_DIRS dinmicamente, por ejemplo:
import os.path TEMPLATE_DIRS = ( os.path.join(os.path.dirname(__file__), templates ).replace( \\ , / ), )
Este ejemplo usa la variable de Python mgica __file__, la cual es automticamente asignada al nombre del archivo del mdulo de Python en el que se encuentra el cdigo. Con la variable TEMPLATE_DIRS congurada, el prximo paso es cambiar el cdigo de vista que usa la funcionalidad de carga de plantillas de Django, para no incluir la ruta a la plantilla. Volvamos a nuestra vista current_datetime, vamos a cambiar esta como sigue:
from django.template.loader import get_template from django.template import Context from django.http import HttpResponse import datetime
45
def current_datetime(request): now = datetime.datetime.now() t = get_template( current_datetime.html ) html = t.render(Context({ current_date : now})) return HttpResponse(html)
En este ejemplo, usamos la funcin django.template.loader.get_template() en vez de cargar la plantilla desde el sistemas de archivos manualmente. La funcin get_template() toma el nombre de la plantilla como argumento, se da cuenta de dnde est la plantilla en el sistema de archivos, lo abre, y retorna un objeto Template compilado. Si get_template() no puede encontrar la plantilla con el nombre pasado, esta levanta una excepcin TemplateDoesNotExist. Para ver que cmo se ve eso, ejecutar el servidor de desarrollo de Django otra vez, como en el Captulo 3, ejecutando python manage.py runserver en el directorio de tu proyecto de Django. Luego, escribe en tu navegador la pgina que activa la vista current_datetime (o sea, http://127.0.0.1:8000/time/). Asumiendo que tu variable de conguracin DEBUG est asignada a True y todava no has creado la plantilla current_datetime.html, deberas ver una pgina de error de Django resaltando el error TemplateDoesNotExist.
Figura 4-1: La pgina de error que se muestra cuando una plantilla no se encuentra Esta pgina de error es similar a la que explicamos en el Captulo 3, con una pieza adicional de informacin de depuracin: una seccin Postmortem del cargador de plantillas. Esta seccin te indica qu plantilla intent cargar Django acompaado de una razn para cada intento fallido (por ej. File does not exist). Esta informacin es invaluable cuando hacemos depuracin de errores de carga de plantillas. Como probablemente puedas distinguir de los mensajes de error de la Figura 41, Django intent buscar una plantilla combinando el directorio de la variable TEMPLATE_DIRS con el nombre de la plantilla pasada a get_template(). Entonces si tu variable TEMPLATE_DIRS contiene /home/django/templates, Django buscar /home/django/templates/current_datetime.html. Si TEMPLATE_DIRS contiene ms que un directorio, cada uno de estos es examinado hasta que se encuentre la plantilla o hasta que no haya ms directorios. Continuando, crea el archivo current_datetime.html en tu directorio de plantillas usando el siguiente cdigo:
<html><body>It is now {{ current_date }}.</body></html>
46
5.6.1 render_to_response()
Debido a que es comn cargar una plantilla, rellenar un Context, y retornar un objeto HttpResponse con el resultado de la plantilla renderizada, Django provee un atajo que te deja hacer estas cosas en una lnea de cdigo. Este atajo es la funcin llamada render_to_response(), la cual se encuentra en el mdulo django.shortcuts. La mayora de las veces, usars render_to_response() en vez de cargar las plantillas y crear los objetos Context y HttpResponse manualmente. Aqu est el ejemplo actual current_datetime reescrito utilizando render_to_response():
from django.shortcuts import render_to_response import datetime def current_datetime(request): now = datetime.datetime.now() return render_to_response( current_datetime.html , { current_date : now})
Qu diferencia! Vamos paso a paso a travs de los cambios del cdigo: No tenemos que importar get_template, Template, Context, o HttpResponse. En vez de esto, importamos django.shortcuts.render_to_response. import datetime se mantiene. En la funcin current_datetime, seguimos calculando now, pero la carga de la plantilla, creacin del contexto, renderizacin de esta, y de la creacin de HttpResponse se encarga la llamada a render_to_response(). Como render_to_response() retorna un objeto HttpResponse, podemos simplemente retornar ese valor en la vista. El primer argumento de render_to_response() debe ser el nombre de la plantilla a utilizar. El segundo argumento, si es pasado, debe ser un diccionario para usar en la creacin de un Context para esa plantilla. Si no se le pasa un segundo argumento, render_to_response() utilizar un diccionario vaco.
Muchas veces, como en este ejemplo, buscars t mismo calcular algunos valores, guardando ellos en variables (por ej. now en el cdigo anterior), y pasando estas a la plantilla. Particularmente los programadores perezosos notarn que es ligeramente redundante tener esos nombres en variables temporales y tener nombres para las variables de la plantilla. No slo que esto es redundante, sino que tambin hay que teclear ms. Entonces si eres uno de esos programadores perezosos y quieres ahorrar cdigo particularmente conciso, puedes tomar la ventaja de la funcin built-in de Python llamada locals(). Esta retorna un diccionario mapeando todos los nombres de variables locales con sus valores. De esta manera, la vista anterior podra reescribirse como sigue:
def current_datetime(request): current_date = datetime.datetime.now() return render_to_response( current_datetime.html , locals())
Aqu, en vez de especicar manualmente el diccionario al contexto como antes, pasamos el valor de locals(), el cual incluye todas las variables denidas hasta ese punto en la ejecucin de la funcin. Como una consecuencia, renombramos el nombre de la variable now a current_date, porque esta es la variable que especicamos en la plantilla. En este ejemplo, locals() no ofrece una gran mejora, pero esta tcnica puede ahorrar un poco de tipeo si tienes plantillas con varias variables denidas o si eres perezoso. 5.6. Cargadores de plantillas 47
Una cosa en la que tiene que tener cuidado cuando usas locals() es que esta incluye todas las variables locales, con lo cual quizs conste de ms variables de las cuales quieres tener acceso en la plantilla. En el ejemplo anterior, locals() tambin incluir request. Depende de tu aplicacin saber si esto es de importancia. La ltima cosa a considerar es que locals() provoca un poco sobrecarga, porque cuando es llamado, Python crea el diccionario dinmicamente. Si especicas el diccionario al contexto manualmente, evitas esta sobrecarga.
Debido a que render_to_response() es un pequeo envoltorio de get_template(), puedes hacer lo mismo con el primer argumento de render_to_response(). No hay lmites para la profundidad del rbol de subdirectorios. Sintete libre de usar tantos como quieras. Nota: Los usuario de Windows, asegrense de usar barras comunes en vez de barras invertidas. get_template() asume el estilo de designacin de archivos de Unix.
Este ejemplo incluye el contenido de la plantilla cuyo nombre se encuentra en la variable template_name:
{% include template_name %}
Como en get_template(), el nombre del archivo de la plantilla es determinado agregando el directorio de plantillas tomado de TEMPLATE_DIRS para el nombre de plantilla solicitado. 48 Captulo 5. Captulo 4: El sistema de plantillas de Django
Las plantillas incluidas son evaluadas con el contexto de la plantilla en la cual est incluida. Si una plantilla no encuentra el nombre tomado, Django har una de estas dos cosas: Si DEBUG es True, vers la excepcin TemplateDoesNotExist sobre la pgina de error de Django. Si DEBUG es False, la etiqueta fallar silenciosamente, sin mostrar nada en el lugar de la etiqueta.
Esto se ve bien, pero Qu sucede cuando queremos crear una plantilla para otra vista digamos, La vista hours_ahead del Captulo 3? Si queremos hacer nuevamente una agradable, vlida, y completa plantilla HTML, crearamos algo como:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang= "en" > <head> <title>Future time</title> </head> <body> <h1>My helpful timestamp site</h1> <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p> <hr> <p>Thanks for visiting my site.</p> </body> </html>
Claramente, estaramos duplicando una cantidad de cdigo HTML. Imagina si tendramos ms sitios tpicos, incluyendo barra de navegacin, algunas hojas de estilo, quizs algo de JavaScript terminaramos poniendo todo tipo de HTML redundante en cada plantilla.
49
La solucin a este problema usando includes en el servidor es sacar factor comn de ambas plantillas y guardarlas en recortes de plantillas separados, que luego son incluidos en cada plantilla. Quizs quieras guardar la parte superior de la plantilla en un archivo llamado header.html:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang= "en" > <head>
Con una estrategia basada en includes, la cabecera y la parte de abajo son fciles. Es el medio el que queda desordenado. En este ejemplo, ambas pginas contienen un ttulo <h1>My helpful timestamp site</h1> pero ese ttulo no puede encajar dentro de header.html porque <title> en las dos pginas es diferente. Si incluimos <h1> en la cabecera, tendramos que incluir <title>, lo cual no permitira personalizar este en cada pgina. Ves a dnde queremos llegar? El sistema de herencia de Django soluciona estos problemas. Lo puedes pensar a esto como la versin contraria a la del lado del servidor. En vez de denir los pedazos que son comunes, denes los pedazos que son diferentes. El primer paso es denir una plantilla base un esquelete de tu pgina que las plantillas hijas llenaran luego. Aqu hay una platilla para nuestro ejemplo actual:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang= "en" > <head> <title>{% block title %}{% endblock %}</title> </head> <body> <h1>My helpful timestamp site</h1> {% block content %}{% endblock %} {% block footer %} <hr> <p>Thanks for visiting my site.</p> {% endblock %} </body> </html>
Esta plantilla, que llamamos base.html, dene un documento esqueleto HTML simple que usaremos para todas las pginas del sitio. Es trabajo de las plantillas hijas sobreescribir, agregar, dejar vaco el contenido de los bloques. (Si ests lo siguiendo desde casa, guarda este archivo en tu directorio de plantillas). Usamos una etiqueta de plantilla aqu que no hemos visto antes: la etiqueta { % block %}. Todas las etiquetas { % block %} le indican al motor de plantillas que una plantilla hijo quizs sobreescriba esa porcin de la plantilla. Ahora que tenemos una plantilla base, current_datetime.html para usar esto:
{% extends " base.html " %} {% block title %}The current time{% endblock %} {% block content %} <p>It is now {{ current_date }}.</p> {% endblock %}
podemos
modicar
nuestra
plantilla
existente
Como estamos en este tema, vamos a crear una plantilla para la vista hours_ahead del Captulo 3. (Si lo ests siguiendo junto con el cdigo, te dejamos cambiar hours_ahead para usar el sistema de 50 Captulo 5. Captulo 4: El sistema de plantillas de Django
No es hermoso? Cada plantilla contiene slo el cdigo que es nico para esa plantilla. No necesita redundancia. Si necesitas hacer un cambio grande en el diseo del sitio, slo cambia base.html, y todas las otras plantillas reejarn el efecto inmediatamente. Veamos cmo trabaja. Cuando cargamos una plantilla current_datetime.html, el motor de plantillas ve la etiqueta { % extends %}, nota que esta plantilla es la hija de otra. El motor inmediatamente carga la plantilla padre en este caso, base.html. Hasta este punto, el motor de la plantilla nota las tres etiquetas { % block %} en base.html y reemplaza estos bloques por el contenido de la plantilla hija. Entonces, el ttulo que denimos en { % block title %} ser usado, as como { % block content %}. Nota que desde la plantilla hija no denimos el bloque footer, entonces el sistema de plantillas usa el valor desde la plantilla padre. El contenido de la etiqueta { % block %} en la plantilla padre es siempre usado como un plan alternativo. La herencia no afecta el funcionamiento del contexto, y puedes usar tantos niveles de herencia como necesites. Una forma comn de utilizar la herencia es el siguiente enfoque de tres niveles: 1. Crear una plantilla base.html que contenga el aspecto principal de tu sitio. Esto es lo que rara vez cambiar, si es que alguna vez cambia. 2. Crear una plantilla base_SECTION.html para cada seccin de tu sitio (por ej. base_photos.html y base_forum.html). Esas plantillas heredan de base.html e incluyen secciones especcas de estilo/diseo. 3. Crear una plantilla individual para cada tipo de pgina, tales como pginas de formulario o galera de fotos. Estas plantillas heredan de la plantilla de la seccin apropiada. Esta aproximacin maximiza la reutilizacin de cdigo y hace fcil el agregado de elementos para compartir reas, como puede ser un navegador de seccin. Aqu hay algunos consejos para el trabajo con herencia de plantillas: Si usas { % extends %} en la plantilla, esta debe ser la primer etiqueta de esa plantilla. En otro caso, la herencia no funcionar. Generalmente, cuanto ms etiquetas { % block %} tengas en tus plantillas, mejor. Recuerda, las plantillas hijas no tienen que denir todos los bloques del padre, entonces puedes rellenar un nmero razonable de bloques por omisin, y luego denir slo lo que necesiten las plantillas hijas. Es mejor tener ms conexiones que menos. Si encuentras cdigo duplicado en un nmero de plantillas, esto probablemente signique que debes mover ese cdigo a un { % block %} en la plantilla padre. Si necesitas obtener el contenido de un bloque desde la plantilla padre, la variable {{ block.super }} har este truco. Esto es til si quieres agregar contenido del bloque padre en vez de sobreescribirlo completamente. No puedes denir mltiples etiquetas { % block %} con el mismo nombre en la misma plantilla. Esta limitacin existe porque una etiqueta bloque trabaja en ambas direcciones. Esto es, una etiqueta bloque no slo provee un agujero a llenar, sino que tambin dene el contenido que llenar ese agujero en el padre. Si hay dos nombres similares de etiquetas { % block %} en una plantilla, el padre de esta plantilla puede no saber cual de los bloques usar. El nombre de plantilla pasado a { % extends %} es cargado usando el mismo mtodo que get_template(). Esto es, el nombre de la plantilla es agregado a la variable TEMPLATE_DIRS. 5.7. Herencia de plantillas 51
En la mayora de los casos, el argumento para { % extends %} ser un string, pero tambin puede ser una variable, si no sabes el nombre de la plantilla padre hasta la ejecucin. Esto te permite hacer cosas divertidas, dinmicas.
5.8 Qu sigue?
Los sitios web ms modernos son manejados con una base de datos: el contenido de la pgina web est guardado en una base de datos relacional. Esto permite una clara separacin de los datos y la lgica (de la misma manera que las vistas y las etiquetas permiten una separacin de la lgica y la vista). El prximo captulo cubre las herramientas que Django brinda para interactuar con la base de datos.
52
CAPTULO 6
En el Captulo 3, recorrimos los conceptos fundamentales de la construccin dinmica de sitios web con Django: La conguracin de vistas y URLconfs. Como explicamos, una vista es responsable de implementar alguna lgica arbitraria, y luego retornar una respuesta. En el ejemplo, nuestra lgica arbitraria era calcular la fecha y hora actual. En las aplicaciones web modernas, la lgica arbitraria a menudo implica interactuar con una base de datos. Detrs de escena, un sitio web impulsado por una base de datos se conecta a un servidor de base de datos, recupera algunos datos de esta, y los muestra con un formato agradable en una pgina web. O, del mismo modo, el sitio puede proporcionar funcionalidad que permita a los visitantes del sitio poblar la base de datos por su propia cuenta. Muchos sitios web ms complejos proporcionan alguna combinacin de las dos. Amazon.com, por ejemplo, es un gran ejemplo de un sitio que maneja una base de datos. Cada pgina de un producto es esencialmente una consulta a la base de datos de productos de Amazon formateada en HTML, y cuando envas una opinin de cliente (customer review), esta es insertada en la base de datos de opiniones. Django es apropiado para crear sitios web que manejen una base de datos, ya que incluye una manera fcil pero poderosa de realizar consultas a bases de datos utilizando Python. Este captulo explica esta funcionalidad: la capa de la base de datos de Django. (Nota: Aunque no es estrictamente necesario conocer teora bsica de bases de datos y SQL para usar la capa de base de datos de Django, es altamente recomendado. Una introduccin a estos conceptos est ms all del alcance de este libro, pero contina leyendo si eres nuevo en el tema. Probablemente seas capaz de seguir adelante y captar los conceptos bsicos en base al contexto).
6.1 La manera tonta de hacer una consulta a la base de datos en las vistas
As como en el Captulo 3 detallamos la manera tonta de producir una salida con la vista (codicando en duro) el texto directamente dentro de la vista), hay una manera tonta de recuperar datos desde la base de datos en una vista. Esto es simple: slo usa una biblioteca de Python existente para ejecutar una consulta SQL y haz algo con los resultados. En este ejemplo de vista, usamos la biblioteca MySQLdb (disponible en http://www.djangoproject.com/r/python-mysql/) para conectarnos a una base de datos de MySQL, recuperar algunos registros, y alimentar con ellos una plantilla para mostrar una pgina web:
53
from django.shortcuts import render_to_response import MySQLdb def book_list(request): db = MySQLdb.connect(user= me , db= mydb , passwd= secret , host= localhost ) cursor = db.cursor() cursor.execute( SELECT name FROM books ORDER BY name ) names = [row[0] for row in cursor.fetchall()] db.close() return render_to_response( book_list.html , { names : names})
Este enfoque funciona, pero deberan hacerse evidentes inmediatamente algunos problemas: Estamos codicando en duro (hard-coding) los parmetros de la conexin a la base de datos. Lo ideal sera que esos parmetros se guardarsen en la conguracin de Django. Tenemos que escribir una cantidad de cdigo estereotpico: crear una conexin, un cursor, ejecutar una sentencia, y cerrar la conexin. Lo ideal sera que todo lo que tuviramos que hacer fuera especicar los resultados que queremos. Nos ata a MySQL. Si, en el camino, cambiamos de MySQL a PostgreSQL, tenemos que usar un adaptador de base de datos diferente (por ej. psycopg en vez de MySQLdb), alterar los parmetros de conexin y dependiendo de la naturaleza de las sentencia de SQL posiblemente reescribir el SQL. La idea es que el servidor de base de datos que usemos est abstrado, entonces el pasarnos a otro servidor podra signicar realizar un cambio en un nico lugar. Como esperabas, la capa de la base de datos de Django apunta a resolver estos problemas. Este es un adelanto de cmo la vista anterior puede ser reescrita usando la API de Django:
from django.shortcuts import render_to_response from mysite.books.models import Book def book_list(request): books = Book.objects.order_by( name ) return render_to_response( book_list.html , { books : books})
Explicaremos este cdigo enseguida en este captulo. Por ahora, tengamos slo una idea de cmo es.
hacer una abstraccin del acceso a la base de datos, luego vamos a tener una capa que se encarga de mostrar los datos, y vamos a poner una capa en el medio para que regule esto, puedes sacar provecho de un vocabulario compartido y decir, Vamos a usar un patrn MVC aqu. Django sigue el patrn MVC tan al pie de la letra que puede ser llamado un framework MVC. Someramente, la M, V y C se separan en Django de la siguiente manera: M, la porcin de acceso a la base de datos, es manejada por la capa de la base de datos de Django, la cual describiremos en este captulo. V, la porcin que selecciona qu datos mostrar y cmo mostrarlos, es manejada por la vista y las plantillas. C, la porcin que delega a la vista dependiendo de la entrada del usuario, es manejada por el framework mismo siguiendo tu URLconf y llamando a la funcin apropiada de Python para la URL obtenida. Debido a que la C es manejada por el mismo framework y la parte ms emocionante se produce en los modelos, las plantillas y las vistas, Django es conocido como un Framework MTV. En el patrn de diseo MTV, M signica Model (Modelo), la capa de acceso a la base de datos. Esta capa contiene toda la informacin sobre los datos: cmo acceder a estos, cmo validarlos, cul es el comportamiento que tiene, y las relaciones entre los datos. T signica Template (Plantilla), la capa de presentacin. Esta capa contiene las decisiones relacionadas a la presentacin: como algunas cosas son mostradas sobre una pgina web o otro tipo de documento. V signica View (Vista), la capa de la lgica de negocios. Esta capa contiene la lgica que accede al modelo y la delega a la plantilla apropiada: puedes pensar en esto como un puente entre el modelos y las plantillas. Si ests familiarizado con otros frameworks de desarrollo web MVC, como Ruby on Rails, quizs consideres que las vistas de Django pueden ser el controlador y las plantillas de Django pueden ser la vista. Esto es una confusin desafortunada a raz de las diferentes interpretaciones de MVC. En la interpretacin de Django de MVC, la vista describe los datos que son presentados al usuario; no necesariamente el cmo se mostrarn, pero si cules datos son presentados. En contraste, Ruby on Rails y frameworks similares sugieren que el trabajo del controlador incluya la decisin de cuales datos son presentados al usuario, mientras que la vista sea estrictamente el cmo sern presentados y no cules. Ninguna de las interpretaciones es ms correcta que otras. Lo importante es entender los conceptos subyacentes.
55
Aqu hay un resumen de cada propiedad. DATABASE_ENGINE le indica a Django qu base de datos utilizar. Si usas una base de datos con Django, DATABASE_ENGINE debe congurarse con un string de los mostrados en la Tabla 5-1. Tabla 5-1. Conguracin de motores de base de datos Conguracin postgresql Base de datos PostgreSQL Adaptador requerido
postgresql_psycopg2
PostgreSQL
mysql
MySQL
MySQLdb, http://www.djangoproject.com/r/pytho mysql/. No necesita adaptador si se usa Python 2.5+. En caso contrario, pysqlite, http://www.djangoproject.com/r/pythonsqlite/.
sqlite3
SQLite
ado_mssql
oracle
Oracle
NOTA: Cualquiera sea la base de datos que uses, necesitars descargar e instalar el adaptador apropiado. Cada uno de estos est disponible libremente en la web; slo sigue el enlace en la columna Adaptador requerido en la Tabla 5-1. DATABASE_NAME la indica a Django el nombre de tu base de datos. Si ests usando SQLite, especica la ruta completo del sistema de archivos hacia el archivo de la base de datos (por ej. /home/django/mydata.db). DATABASE_USER le indica a Django cual es el nombre de usuario a usar cuando se conecte con tu base de datos. Si ests usando SQLite, deja este en blanco. DATABASE_PASSWORD le indica a Django cual es la contrasea a utilizar cuando se conecte con tu base de datos. Si ests utilizando SQLite o tienes una contrasea vaca, deja este en blanco. DATABASE_HOST le indica a Django cual es el host a usar cuando se conecta a tu base de datos. Si tu base de datos est sobre la misma computadora que la instalacin de Django (o sea localhost), deja este en blanco. Si ests usando SQLite, deja este en blanco. MySQL es un caso especial aqu. Si este valor comienza con una barra (/) y ests usando MySQL, MySQL se conectar al socket especicado por medio de un socket Unix, por ejemplo:
56
DATABASE_HOST = /var/run/mysql
Si ests utilizando MySQL y este valor no comienza con una barra, entonces este valor es asumido como el host. DATABASE_PORT le indica a Django qu puerto usar cuando se conecte a la base de datos. Si ests utilizando SQLite, deja este en blanco. En otro caso, si dejas este en blanco, el adaptador de base de datos subyacente usar el puerto por omisin acorde al servidor de base de datos. En la mayora de los casos, el puerto por omisin est bien, por lo tanto puedes dejar este en blanco. Una vez que hayas ingresado estas conguraciones, comprubalas. Primero, desde el directorio del proyecto que creaste en el Captulo 2, ejecuta el comando python manage.py shell. Notars que comienza un intrprete interactivo de Python. Las apariencias pueden engaar. Hay una diferencia importante entre ejecutar el comando python manage.py shell dentro del directorio del proyecto de Django y el ms genrico python. El ltimo es el Python shell bsico, pero el anterior le indica a Django cuales archivos de conguracin usar antes de comenzar el shell. 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 informacin de la conexin a la base de datos. Detrs de escena, python manage.py shell simplemente asume que tu archivo de conguracin est en el mismo directorio que manage.py. Hay otras maneras de indicarle a Django qu mdulo de conguracin usar, pero este subttulo lo cubriremos luego. Por ahora, usa python manage.py shell cuando necesites hacer modicaciones especcas a Django. Una vez que hayas entrado al shell, escribe estos comando para probar la conguracin de tu base de datos:
>>> from django.db import connection >>> cursor = connection.cursor()
Si no sucede nada, entonces tu base de datos est congurada correctamente. De lo contrario revisa el mensaje de error para obtener un indicio sobre qu es lo que est mal. La Tabla 5-2 muestra algunos mensajes de error comunes. Tabla 5-2. Mensajes de error de conguracin de la base de datos Mensaje de error You havent set the DATABASE_ENGINE setting yet. Environment variable DJANGO_SETTINGS_MODULE is undened. Error loading _____ module: No module named _____. _____ isnt an available database backend. database _____ does not exist Solucin Congura la variable DATABASE_ENGINE con otra cosa que un string vaco. Ejecuta el comando python manage.py shell en vez de python. No tienes instalado el mdulo apropiado para la base de datos especicada (por ej. psycopg o MySQLdb). Congura la variable DATABASE_ENGINE con un motor vlido descrito previamente. Habrs cometido un error de tipeo? Cambia la variable DATABASE_NAME para que apunte a una base de datos existente, o ejecuta la sentencia CREATE DATABASE apropiada para crearla. Cambia la variable DATABASE_USER para que apunte a un usuario que exista, o crea el usuario en tu base de datos. Asegrate de que DATABASE_HOST y DATABASE_PORT estn congurados correctamente y que el servidor est corriendo.
de Python y representen una aplicacin completa de Django. Vale la pena explicar la terminologa aqu, porque esto es algo que suele hacer tropezar a los principiantes. Ya hemos creado un proyecto, en el Captulo 2, entonces, cul es la diferencia entre un proyecto y una aplicacin? La diferencia es la que existe entre la conguracin y el cdigo: Un proyecto es una instancia de un cierto conjunto de aplicaciones de Django, ms las conguraciones de esas aplicaciones. Tcnicamente, el nico requerimiento de un proyecto es que este suministre un archivo de conguracin, el cual dene la informacin hacia la conexin a la base de datos, la lista de las aplicaciones instaladas, la variable TEMPLATE_DIRS, y as sucesivamente. Una aplicacin es un conjunto portable de una funcionalidad de Django, tpicamente incluye modelos y vistas, que conviven en un solo paquete de Python. Por ejemplo, Django incluye un nmero de aplicaciones, tales como un sistema de comentarios y una interfaz de administracin automtica. Una cosa clave para notar sobre estas aplicaciones es que son portables y reusables en mltiples proyectos. Hay pocas reglas estrictas sobre cmo encajar el cdigo Django en este esquema; es exible. Si ests construyendo un sitio web simple, quizs uses una sola aplicacin. Si ests construyendo un sitio web complejo con varias piezas que no se relacionan entre s, tales como un sistema de comercio electrnico o un foro, probablemente quieras dividir esto en aplicaciones para que te sea posible reusar estas individualmente en un futuro. Es ms, no necesariamente debes crear aplicaciones en absoluto, como lo hace evidente la funcin de la vista del ejemplo que creamos antes en este libro. En estos casos, simplemente creamos un archivo llamado views.py, llenamos este con una funcin de vista, y apuntamos nuestra URLconf a esa funcin. No se necesitan aplicaciones. No obstante, existe un requisito respecto a la convencin de la aplicacin: si ests usando la capa de base de datos de Django (modelos), debes crear una aplicacin de Django. Los modelos deben vivir dentro de aplicaciones. Dentro del directorio del proyecto mysite que creaste en el Captulo 2, escribe este comando para crear una nueva aplicacin llamada books:
python manage.py startapp books
Este comando no produce ninguna salida, pero crea un directorio books dentro del directorio mysite. Echemos un vistazo al contenido:
books/ __init__.py models.py views.py
Esos archivos contendrn los modelos y las vistas para esta aplicacin. Echa un vistazo a models.py y views.py en tu editor de texto favorito. Ambos archivos estn vacos, excepto por la importacin en models.py. Este es el espacio disponible para ser creativo con tu aplicacin de Django.
Si ests familiarizado con base de datos, inmediatamente podra pensar, No es redundante denir modelos de datos en Python y en SQL? Django trabaja de este modo por varias razones: La introspeccin requiere *overhead* y es imperfecta. Con el objetivo de proveer una API conveniente de acceso a los datos, Django necesita conocer de alguna forma la capa de la base de datos, y hay dos formas de lograr esto. La primera sera describir explcitamente los datos en Python, y la segunda sera la introspeccin de la base de datos en tiempo de ejecucin para determinar el modelo de la base de datos. La segunda forma parece clara, porque los metadatos sobre tus tablas vive en un nico lugar, pero introduce algunos problemas. Primero, introspeccionar una base de datos en tiempo de ejecucin obviamente requiere overhead. Si el framework tuviera que introspeccionar la base de datos cada vez que se procese una peticin, o incluso cuando el servidor web sea inicializado, esto podra provocar un nivel de overhead inaceptable. (Mientras algunos creen que el nivel de overhead es aceptable, los desarrolladores de Django apuntan a quitar del framework tanto overhead como sea posible, y esta aproximacin hace que Django sea ms rpido que los frameworks competidores de alto nivel en mediciones de desempeo). Segundo, algunas bases de datos, notablemente viejas versiones de MySQL, no guardan suciente metadatos para asegurarse una completa introspeccin. Escribir Python es divertido, y dejar todo en Python limita el nmero de veces que tu cerebro tiene que realizar un cambio de contexto. Si te mantienes en un solo entorno/mentalidad de programacin tanto tiempo como sea posible, ayuda para la productividad. Teniendo que escribir SQL, luego Python, y luego SQL otra vez es perjudicial. Tener modelos de datos guardados como cdigo en vez de en tu base de datos hace fcil dejar tus modelos bajo un control de versiones. De esta forma, puedes fcilmente dejar rastro de los cambios a tu capa de modelos. SQL permite slo un cierto nivel de metadatos acerca de un *layout* de datos. La mayora de sistemas de base de datos, por ejemplo, no provee un tipo de datos especializado para representar una direccin web o de email. Los modelos de Django s. La ventaja de un tipo de datos de alto nivel es la alta productividad y la reusabilidad de cdigo. SQL es inconsistente a travs de distintas plataformas. Si ests redistribuyendo una aplicacin web, por ejemplo, es mucho ms pragmtico distribuir un mdulo de Python que describa tu capa de datos que separar conjuntos de sentencias CREATE TABLE para MySQL, PostgreSQL y SQLite. Una contra de esta aproximacin, sin embargo, es que es posible que el cdigo Python quede fuera de sincrona respecto a lo que hay actualmente en la base. Si haces cambios en un modelo Django, necesitars hacer los mismos cambios dentro de tu base de datos para mantenerla consistente con el modelo. Detallaremos algunas estrategias para manejar este problema ms adelante en este captulo. Finalmente, Django incluye una utilidad que puede generar modelos haciendo introspeccin sobre una base de datos existente. Esto es til para comenzar a trabajar rpidamente sobre datos heredados.
Un libro tiene un ttulo y una fecha de publicacin. Tambin tiene uno o ms autores (una relacin muchos-a-muchos con autores) y un nico editor (una relacin uno a muchos tambin conocida como clave fornea con editores). El primer paso para utilizar esta conguracin de base de datos con Django es expresarla como cdigo Python. En el archivo models.py que se cre con el comando startapp, ingresa lo siguiente:
from django.db import models 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() class Author(models.Model): salutation = models.CharField(maxlength=10) first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=40) email = models.EmailField() headshot = models.ImageField(upload_to= /tmp ) class Book(models.Model): title = models.CharField(maxlength=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField()
Examinemos rpidamente este cdigo para conocer lo bsico. La primer cosa a notar es que cada modelo es representado por una clase Python que es una subclase de django.db.models.Model. La clase antecesora, Model, 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 slo sean responsables de denir sus campos, en una sintaxis compacta y agradable. Lo creas o no, ste es todo el cdigo que necesitamos para tener acceso bsico a los datos con Django. Cada modelo generalmente corresponde a una tabla nica de la base de datos, y cada atributo de un modelo generalmente corresponde a una columna en esa tabla. El nombre de atributo corresponde al nombre de columna, y el tipo de campo (ej.: CharField) corresponde al tipo de columna de la base de datos (ej.: varchar). Por ejemplo, el modelo Publisher es equivalente a la siguiente tabla (asumiendo la sintaxis de PostgreSQL para CREATE TABLE):
CREATE TABLE "books_publisher" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(30) NOT NULL, "address" varchar(50) NOT NULL, "city" varchar(60) NOT NULL, "state_province" varchar(30) NOT NULL, "country" varchar(50) NOT NULL, "website" varchar(200) NOT NULL );
En efecto, Django puede generar esta sentencia CREATE TABLE automticamente como veremos en un momento. La excepcin a la regla una-clase-por-tabla es el caso de las relaciones muchos-a-muchos. En nuestros modelos de ejemplo, Book tiene un ManyToManyField llamado authors. Esto signica que un libro tiene uno o ms autores, pero la tabla de la base de datos Book no tiene una columna authors. En su lugar, Django crea una tabla adicional una tabla de join muchos-a-muchos que maneja la correlacin entre libros y autores.
60
Para una lista completa de tipos de campo y opciones de sintaxis de modelos, ver el Apndice B. Finalmente, debes notar que no hemos denido explcitamente una clave primaria en ninguno de estos modelos. A no ser que le indiques lo contrario, Django dar automticamente a cada modelo un campo de clave primaria entera llamado id. Es un requerimiento el que cada modelo Django tenga una clave primaria de columna simple.
Temporalmente, comenta estos cuatro strings poniendo un carcter (#) al principio. (Estn incluidos por omisin porque es frecuente usarlas, pero las activaremos y las discutiremos ms adelante) Cuando termines, modica las conguraciones por omisin de MIDDLEWARE_CLASSES y TEMPLATE_CONTEXT_PROCESSORS. stas dependen de algunas de las aplicaciones que hemos comentado. Entonces, agrega mysite.books a la lista INSTALLED_APPS, de manera que la conguracin termine vindose as:
MIDDLEWARE_CLASSES = ( # django.middleware.common.CommonMiddleware, # django.contrib.sessions.middleware.SessionMiddleware, # django.contrib.auth.middleware.AuthenticationMiddleware, # django.middleware.doc.XViewMiddleware, ) TEMPLATE_CONTEXT_PROCESSORS = () #... INSTALLED_APPS = ( #django.contrib.auth, #django.contrib.contenttypes, #django.contrib.sessions, #django.contrib.sites, mysite.books , )
(Como aqu estamos tratando con una tupla de un solo elemento, no olvides la coma nal. De paso, los autores de este libro preeren poner una coma despus de cada elemento de una tupla, aunque la tupla tenga slo un elemento. Esto evita el problema de olvidar comas, y no hay penalizacin por el use de esa coma extra) mysite.books se reere a la aplicacin books en la que estamos trabajando. Cada aplicacin en INSTALLED_APPS es representada por su ruta Python completa esto es, la ruta de paquetes, separados por puntos, que lleva al paquete de la aplicacin. Ahora que la aplicacin Django ha sido activada en el archivo de conguracin, podemos crear las tablas en nuestra base de datos. Primero, validemos los modelos ejecutando este comando:
61
El comando validate verica si la sintaxis y la lgica de tus modelos son correctas. Si todo est bien, vers el mensaje 0 errors found. Si no, asegrate de haber escrito el cdigo del modelo correctamente. La salida del error debe brindarte informacin til acerca de qu es lo que est mal en el cdigo. Cada vez que piensas que tienes problemas con tus modelos, ejecuta python manage.py validate. Tiende a capturar todos los problemas comunes del modelo. Si tus modelos son vlidos, ejecuta el siguiente comando para que Django genere sentencias CREATE TABLE para tus modelos en la aplicacin books (con sintaxis resaltada en colores disponible si ests usando Unix):
python manage.py sqlall books
En este comando, books es el nombre de la aplicacin. Es lo que hayas especicado cuando ejecutaste el comando manage.py startapp. Cuando ejecutes el comando, debes ver algo como esto:
BEGIN; CREATE TABLE "books_publisher" ( "id" serial NOT NULL PRIMARY KEY, "name" varchar(30) NOT NULL, "address" varchar(50) NOT NULL, "city" varchar(60) NOT NULL, "state_province" varchar(30) NOT NULL, "country" varchar(50) NOT NULL, "website" varchar(200) NOT NULL ); 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 ); CREATE TABLE "books_author" ( "id" serial NOT NULL PRIMARY KEY, "salutation" varchar(10) NOT NULL, "first_name" varchar(30) NOT NULL, "last_name" varchar(40) NOT NULL, "email" varchar(75) NOT NULL, "headshot" varchar(100) NOT NULL ); CREATE TABLE "books_book_authors" ( "id" serial NOT NULL PRIMARY KEY, "book_id" integer NOT NULL REFERENCES "books_book" ( "id" ), "author_id" integer NOT NULL REFERENCES "books_author" ( "id" ), UNIQUE ( "book_id" , "author_id" ) ); CREATE INDEX books_book_publisher_id ON "books_book" ( "publisher_id" ); COMMIT;
Observa lo siguiente: Los nombres de tabla se generan automticamente combinando el nombre de la aplicacin (books) y el nombre en minsculas del modelo (publisher, book, y author). Puedes sobreescribir este comportamiento, como se detalla en el Apndice B. Como mencionamos antes, Django agrega una clave primaria para cada tabla automticamente los campos id. Tambin puedes sobreescribir esto.
62
Por convencin, Django agrega "_id" al nombre de campo de las claves forneas. Como ya puedes imaginar, tambin puedes sobreescribir esto. La relacin de clave fornea se hace explcita con una sentencia REFERENCES Estas sentencias CREATE TABLE son adaptadas a medida de la base de datos que ests usando, de manera que Django maneja automticamente los tipos de campo especcos de cada base de datos, como auto_increment (MySQL), serial (PostgreSQL), o integer primary key (SQLite), por ti. Lo mismo sucede con el uso de las comillas simples o dobles en los nombres de columna. La salida del ejemplo est en la sintaxis de PostgreSQL. El comando sqlall no crea ni toca de ninguna forma tu base de datos slo imprime una salida en la pantalla para que puedas ver qu SQL ejecutara Django si le pidieras que lo hiciera. Si quieres, puedes copiar y pegar este fragmento de SQL en tu cliente de base de datos, o usa los pipes de Unix para pasarlo directamente. De todas formas, Django provee una manera ms fcil de conrmar el envo del SQL a la base de datos. Ejecuta el comando syncdb de esta manera:
python manage.py syncdb
El comando syncdb es una simple sincronizacin de tus modelos hacia tu base de datos. El mismo examina todos los modelos en cada aplicacin que gure en tu variable de conguracin INSTALLED_APPS, verica la base de datos para ver si las tablas apropiadas ya existen, y las crea si no existen. Observa que syncdb no sincroniza los cambios o eliminaciones de los modelos; si haces un cambio o modicas un modelo, y quieres actualizar la base de datos, syncdb no maneja esto. (Ms sobre esto despus.) Si ejecutas python manage.py syncdb de nuevo, nada sucede, porque no has agregado ningn modelo a la aplicacin books ni has incorporado ninguna aplicacin en INSTALLED_APPS. Ergo, siempre es seguro ejecutar python manage.py syncdb no har desaparecer cosas. Si ests interesado, toma un momento para bucear en el cliente de lnea de comandos de tu servidor de bases de datos y ver las tablas que cre Django. Puedes ejecutar manualmente el cliente de lnea de comandos (ej.: psql para PostgreSQL) o puedes ejecutar el comando python manage.py dbshell, que deducir qu cliente de linea de comando ejecutar, dependiendo de tu conguracin DATABASE_SERVER. Esto ltimo es casi siempre ms conveniente.
63
Estas pocas lneas logran bastantes resultados. Estos son los puntos salientes: Para crear un objeto, slo importa la clase del modelo apropiada y crea una instancia pasndole valores para cada campo. Para guardar el objeto en la base de datos, llama el mtodo save() del objeto. Detrs de la escena, Django ejecuta aqu una sentencia SQL INSERT. Para recuperar objetos de la base de datos, usa el atributo Publisher.objects. Busca una lista de todos los objetos Publisher en la base de datos con la sentencia Publisher.objects.all(). Detrs de escenas, Django ejecuta aqu una sentencia SQL SELECT. Naturalmente, puedes hacer mucho con la API de base de datos de Django pero primero, tengamos cuidado de una pequea incomodidad.
Podemos arreglar esto fcilmente agregando un mtodo llamado __str__() a nuestro objeto Publisher. Un mtodo __str__() le dice a Python como mostrar la representacin string de un objeto. Puedes ver esto en accin agregando un mtodo __str__() a los tres modelos:
from django.db import models 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 Author(models.Model): salutation = models.CharField(maxlength=10) first_name = models.CharField(maxlength=30) last_name = models.CharField(maxlength=40) email = models.EmailField() headshot = models.ImageField(upload_to= /tmp ) **def __str__(self):** %s % (self.first_name, self.last_name)** **return %s class Book(models.Model): title = models.CharField(maxlength=100) authors = models.ManyToManyField(Author) publisher = models.ForeignKey(Publisher) publication_date = models.DateField() **def __str__(self):** **return self.title**
Como puedes ver, un mtodo __str__() puede hacer lo que sea que necesite hacer para devolver una representacin textual. Aqu, los mtodos __str__() de Publisher y Book devuelven simplemente 64 Captulo 6. Captulo 5: Interactuar con una base de datos: Modelos
el nombre y ttulo del objeto respectivamente, pero el __str__() del Author es un poco ms complejo junta los campos first_name y last_name. El nico requerimiento para __str__() es que devuelva un string. Si __str__() no devuelve un string si retorna, digamos, un entero entonces Python generar un TypeError con un mensaje como "__str__ returned non-string". Para que los cambios sean efectivos, sal del shell Python y entra de nuevo con python manage.py shell. (Esta es la manera ms simple de hacer que los cambios en el cdigo tengan efecto.) Ahora la lista de objetos Publisher es ms fcil de entender:
>>> from books.models import Publisher >>> publisher_list = Publisher.objects.all() >>> publisher_list [<Publisher: Addison-Wesley>, <Publisher: OReilly>]
Asegrate de que cada modelo que denas tenga un mtodo __str__() no solo por tu propia conveniencia cuando usas el intrprete interactivo, sino tambin porque Django usa la salida de __str__() en muchos lugares cuando necesita mostrar objetos. Finalmente, observa que __str__() es un buen ejemplo de agregar comportamiento a los modelos. Un modelo Django describe ms que la conguracin de la tabla de la base de datos; tambin describe toda funcionalidad que el objeto sepa hacer. __str__() es un ejemplo de esa funcionalidad un modelo sabe cmo mostrarse.
Este acto de instanciar una clase modelo no toca la base de datos. Para guardar el registro en la base de datos (esto es, para realizar la sentencia SQL INSERT), llama al mtodo save() del objeto:
>>> p.save()
Como el modelo Publisher usa una clave primaria autoincremental id, la llamada inicial a save() hace una cosa ms: calcula el valor de la clave primaria para el registro y lo establece como el valor del atributo id de la instancia:
>>> p.id 52 # esto ser diferente segn tus datos
Las subsecuentes llamadas a save() guardarn el registro en su lugar, sin crear un nuevo registro (es decir, ejecutarn una sentencia SQL UPDATE en lugar de un INSERT):
65
La sentencia save() del prrafo anterior resulta aproximadamente en la sentencia SQL siguiente:
UPDATE book_publisher SET name = Apress Publishing, address = 2855 Telegraph Ave., city = Berkeley, state_province = CA, country = U.S.A., website = http://www.apress.com WHERE id = 52;
Nota: Nota que Django no usa SELECT * cuando busca datos y en cambio lista todos los campos explcitamente. Esto es una decisin de diseo: en determinadas circunstancias SELECT * puede ser lento, y (ms importante) listar los campos sigue el principio del Zen de Python: Explcito es mejor que implcito. Para ms sobre el Zen de Python, intenta escribiendo import this en el prompt de Python. Echemos un vistazo a cada parte de esta linea Publisher.objects.all(): En primer lugar, tenemos nuestro modelo denido, Publisher. Aqu no hay nada extrao: cuando quieras buscar datos, usa el modelo para esto. Luego, tenemos objects. Tcnicamente, esto es un administrador (manager). Los administradores son discutidos en el Apndice B. Por ahora, todo lo que necesitas saber es que los administradores se encargan de todas las operaciones a nivel de tablas sobre los datos incluidos, y lo ms importante, las consultas. Todos los modelos automticamente obtienen un administrador objects; debes usar el mismo cada vez que quieras consultar sobre una instancia del modelo. Finalmente, tenemos all(). Este es un mtodo del administrador objects que retorna todas las las de la base de datos. Aunque este objeto se parece a una lista, es realmente un QuerySet un objeto que representa algn conjunto de las de la base de datos. El Apndice C describe QuerySets en detalle. Para el resto de este captulo, slo trataremos estos como listas emuladas. Cualquier bsqueda en base de datos va a seguir esta pauta general llamaremos mtodos del administrador adjunto al modelo en el cual queremos hacer nuestra consulta.
66
filter() toma argumentos de palabra clave que son traducidos en las clusulas SQL WHERE apropiadas. El ejemplo anterior sera traducido en algo como:
SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE name = Apress Publishing;
Puedes pasarle a filter() mltiples argumentos para reducir las cosas an ms:
>>> Publisher.objects.filter(country= " U.S.A. " , state_province= " CA " ) [<Publisher: Apress Publishing>]
Esos mltiples argumentos son traducidos a clusulas SQL AND. Por lo tanto el ejemplo en el fragmento de cdigo se traduce a lo siguiente:
SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE country = U.S.A. AND state_province = CA;
Notar que por omisin la bsqueda usa el operador SQL = para realizar bsquedas exactas. Existen tambin otros tipos de bsquedas:
>>> Publisher.objects.filter(name__contains= " press " ) [<Publisher: Apress Publishing>]
Notar el doble guin bajo entre name y contains. Del mismo modo que Python, Django usa el doble guin bajo para indicar que algo mgico est sucediendo aqu la parte __contains es traducida por Django en una sentencia SQL LIKE:
SELECT id, name, address, city, state_province, country, website FROM book_publisher WHERE name LIKE %press %;
Hay disponibles varios otos tipos de bsqueda, incluyendo icontains (LIKE no sensible a diferencias de maysculas/minsculas), startswith y endswith, y range (consultas SQL BETWEEN). El Apndice C describe en detalle todos esos tipos de bsqueda.
En lugar de una lista (o ms bien, un QuerySet), este mtodo retorna un objeto individual. Debido a eso, una consulta cuyo resultado sean mltiples objetos causar una excepcin:
>>> Publisher.objects.get(country= " U.S.A. " ) Traceback (most recent call last):
67
Una consulta que no retorne objeto alguno tambin causar una excepcin:
>>> Publisher.objects.get(name= " Penguin " ) Traceback (most recent call last): ... DoesNotExist: Publisher matching query does not exist.
Esto no se ve muy diferente del ejemplo de all() anterior, pero el SQL incluye ahora un ordenamiento especco:
SELECT id, name, address, city, state_province, country, website FROM book_publisher ORDER BY name;
Tambin podemos especicar un ordenamiento inverso antecediendo al nombre del campo un prejo - (el smbolo menos):
>>> Publisher.objects.order_by( " -name " ) [<Publisher: OReilly>, <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 tendrs un campo particular por el que usualmente desears ordenar. Es esos casos Django te permite anexar al modelo un ordenamiento por omisin 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()
68
def __str__(self): return self.name **class Meta:** **ordering = [ " name " ]**
Este fragmento ordering = ["name"] le indica a Django que a menos que se proporcione un ordenamiento mediante order_by(), todos los editores debern 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 Apndice B para conocer las opciones que puede poner bajo Meta.
Como podras esperar, esto se traduce a una consulta SQL conteniendo tanto un WHERE como un ORDER BY:
SELECT id, name, address, city, state_province, .. code-block:: sqlcountry, 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 lmite para esto.
Y ms... Hemos slo mirado la supercie del manejo de modelos, pero deberas ya conocer lo suciente para entender todos los ejemplos del resto del libro. Cuando ests listo para aprender los detalles completos detrs de las bsquedas de objetos, chale una mirada al Apndice C.
69
Puedes tambin borrar objetos al por mayor llamando a delete() en el resultado de algunas bsquedas:
>>> 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 caracterstica 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 slo objetos activos, y simplemente jar el campo activo a False en lugar de eliminar el objeto. Entonces, si descubres que has cometido un error, puedes simplemente volver a conmutar el estado de la bandera.
70
Sin embargo, tenemos aqu un pequeo problema del huevo y la gallina, porque para poder saber cmo 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 usara Django, pero es una buena idea el hacerlo para estar seguros de que todo est en sincrona). La solucin al problema del huevo y la gallina es usar un entorno de desarrollo en lugar de realizar los cambios en un servidor de produccin. (Ests 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 produccin): 1. Agrega el campo a tu modelo. 2. Ejecuta manage.py sqlall [yourapp] para ver la nueva sentencia CREATE TABLE para el modelo. Toma nota de la denicin de la columna para el nuevo campo. 3. Arranca el shell interactivo de tu base de datos (por ej. psql o mysql, o puedes usar manage.py dbshell). Ejecuta una sentencia ALTER TABLE que agregue tu nueva columna. 4. (Opcional) Arranca el shell interactivo de Python con manage.py shell y verica que el nuevo campo haya sido agregado correctamente importando el modelo y seleccionando desde la tabla (por ej. MyModel.objects.all()[:5]). Entonces en el servidor de produccin 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 ests usando un sistema de control de revisiones de cdigo fuente y has realizado un check in de la modicacin del paso 1 del trabajo en el entorno de desarrollo, entonces puedes actualizar el cdigo (por ej. svn update si usas Subversion) en el servidor de produccin. 4. Reinicia el servidor Web para que los cambios en el cdigo surtan efecto. Por ejemplo, hagamos un repaso de los que haramos si agregramos un campo num_pages al modelo Book descrito previamente en este captulo. Primero, alteraramos 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 blank=True y null=True). Luego ejecutaramos el comando manage.py sqlall books para ver la sentencia CREATE TABLE. La misma se vera similar a esto:
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 );
71
A continuacin, arrancaramos el shell interactivo de base de datos en nuestra base de datos de desarrollo escribiendo psql (para PostgreSQL), y ejecutaramos 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 num_pages a nuestro modelo, incluimos las opciones blank=True y null=True. Lo hicimos porque una columna de una base de datos contendr inicialmente valores NULL desde el momento que la crees. Sin embargo, es tambin posible agregar columnas que no puedan contener valores NULL. Para hacer esto, tienes que crear la columna como NULL, luego poblar los valores de la columna usando algunos valor(es) por omisin, y luego modicar la columna para activar el modicador NOT NULL. Por ejemplo:
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 blank=True y null=True de tu modelo. Luego de la sentencia ALTER TABLE, vericaramos que el cambio haya funcionado correctamente, para ello iniciaramos el shell de Python y ejecutaramos este cdigo:
>>> from mysite.books.models import Book >>> Book.objects.all()[:5]
Si dicho cdigo no produjera errores, podramos movernos a nuestro servidor de produccin y ejecutaramos la sentencia ALTER TABLE en la base de datos de produccin. Entonces, actualizaramos el modelo en el entorno de produccin y reiniciaramos el servidor Web.
72
6.14 Qu sigue?
Una vez que has denido tus modelos, el paso siguiente es el poblar tu base de datos con datos. Podras tener datos legados, en cuyo caso el Captulo 16 te aconsejar acerca de cmo integrar bases de datos heredadas. Podras delegar en los usuario del sitio la provisin de los datos, en cuyo caso el Captulo 7 te ensear cmo procesar datos enviados por los usuarios mediante formularios. Pero en algunos casos, t o tu equipo podran necesitar ingresar datos en forma manual, en cuyo caso sera de ayuda el disponer de una interfaz basada en Web para el ingreso y el manejo de los datos. El prximo captulo est dedicado a la interfaz de administracin de Django, la cual existe precisamente por esa razn.
6.14. Qu sigue?
73
74
CAPTULO 7
Para cierto tipo de Sitios Web, una interfaz de administracin es una parte esencial de la infraestructura. Se trata de una interfaz basada en web, limitada a los administradores 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 administracin. Aunque hay un problema con las interfaces de administracin: es aburrido construirlas. El desarrollo web es divertido cuando ests desarrollando funcionalidades de lado pblico del sitio, pero construir interfaces de administracin es siempre lo mismo. Tienes que autenticar usuarios, mostrar y manipular formularios, validar las entradas y dems. Es aburrido y repetitivo. Cul es la solucin de Django para estas tareas aburridas y repetitivas? Las hace todas por ti en slo un par de lneas de cdigo, ni ms ni menos. Con Django, construir interfaces de administracin es un problema resuelto. Este captulo trata sobre la interfaz de administracin automtica de Django. Esta caracterstica funciona leyendo los meta-datos en tus modelos para brindar una interfaz potente y lista para produccin que los administradores del sitio podrn usar inmediatamente. Aqu discutimos cmo activar, usar y personalizar esta utilidad.
75
publication_date = models.DateField() num_pages = models.IntegerField(blank=True, null=True) def __unicode__(self): return self.title **class Admin:** **pass**
La declaracin de Admin marca la clase como poseedora de una interfaz de administracin. Hay una serie de opciones que podemos incluir bajo Admin, pero por ahora vamos a limitarnos al comportamiento por defecto, as que escribimos pass para decirle a Python que la clase Admin est vaca. Si ests siguiendo este ejemplo escribiendo tu propio cdigo, probablemente sea buena idea agregar ahora declaraciones de Admin a las clases Publisher y Author. 2. Instalar la aplicacin admin. Esto se hace agregando "django.contrib.admin" a tus INSTALLED_APPS de tu archivo de conguracin settings.py. 3. Adems, asegurate de que las aplicaciones "django.contrib.sessions", "django.contrib.auth", y "django.contrib.contenttypes" no estn comentadas, ya que la aplicacin admin depende de ellas. Tambin descomenta todas las lneas de MIDDLEWARE_CLASSES congurando la tupla, y borra la denicin de TEMPLATE_CONTEXT_PROCESSOR para permitir que tome los valores por defecto. 4. Ejecuta python manage.py syncdb. Este paso instalar las tablas de la base de datos que la interfaz de administracin necesita. Nota: Es probable que la primera vez que ejecutaste syncdb con "django.contrib.auth" en INSTALLED_APPS, te preguntara algo sobre crear un superusuario. Si no lo hiciste en ese momento, tendrs que ejecutar: python manage.py createsuperuser para crear este usuario administrador. En caso contrario no sers capaz de identicarte para entrar a la interfaz de administracin. 5. Agrega el patrn de URL en tu urls.py. Si an ests usando el que fue creado por startproject, el patrn de la URL de administracin ya debera estar ah, pero comentado. De cualquier forma, los patrones de URL deberan terminar siendo algo as:
from django.conf.urls.defaults import * urlpatterns = patterns( , **( r ^admin/ , include( django.contrib.admin.urls )),** )
Eso es todo. Ahora ejecuta python manage.py runserver para iniciar el servidor de pruebas. Vers algo como esto:
Validating models... 0 errors found. Django version 1.4, using settings mysite.settings Development server is running at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Ahora puedes visitar la URL que te brinda Django (http://127.0.0.1:8000/admin/ en el ejemplo precedente), identicarte, y jugar un poco.
sobre sus caractersticas. Lo primero que vers es una pgina de identicacin, como se muestra en la Figura 6-1.
Figura 6-1. Pantalla de autenticacin de Django. Usars el nombre de usuario y la clave que conguraste cuando agregaste tu superusuario. Una vez identicado, vers que puedes gestionar usuarios, grupos y permisos (veremos ms sobre esto en breve). Cada objeto al que se le di una declaracin Admin aparece en el ndice de la pgina principal, como se muestra en la Figura 6-2
77
Figura 6-2. El ndice principal de la Administracin de Django. Los enlaces para agregar y modicar objetos llevan a dos pginas a las que nos referiremos como listas de cambio 1 y formularios de edicin 2 de objetos: Las listas de cambio son esencialmente pginas de ndices de objetos en el sistema, como se muestra en la Figura 6-3.
1 2
N. del T.: change list es el nombre que recibe en ingls N. del T.: edit forms es el nombre que recibe en ingls
78
Figura 6-3. Una tpica vista de lista de cambio Hay varias opciones que pueden controlar los campos que aparecen en esas listas y la aparicin de caractersticas extra como campos de bsqueda e accesos directo a ltros predenidos. Ms adelante hablaremos sobre esto. Los formularios de edicin se usan para modicar objetos existente y crear nuevos (mira la Figura 64). Cada campo denido en tu modelo aparece aqu, y notars que campos de tipos diferentes tienen diferentes controles. (Por ejemplo, los campos de fecha/hora tienen controles tipo calendario, las claves forneas usan cajas de seleccin, etc.)
79
Figura 6-4. Un tpico formulario de edicin Te dars cuenta que la interfaz de administracin tambin controla por ti la validez de los datos ingresados. Intenta dejar un campo requerido en blanco o poner una fecha invlida en un campo de fecha, y vers esos avisos de esos errores cuando intentes guardar el objeto, como se muestra en la Figura 6-5.
80
Figura 6-5. Un formulario de edicin mostrando errores Cuando editas un objeto existente, vers el botn Historia en la esquina superior derecha de la ventana. Cada cambio realizado a travs de la interfaz de administracin es registrado, y puedes examinar este registro haciendo click en este botn (mira la Figura 6-6).
81
Figura 6-6. Pgina de historia de un objeto django. Cuando eliminas un objeto existente, la interfaz de administracin solicita una conrmacin para prevenir costosos errores. La eliminacin de un objeto se desencadena en cascada, y la pgina de conrmacin de eliminacin del objeto muestra todos los objetos relacionados que se eliminarn con l (mira la Figura 6-7).
82
83
de administracin tiene tres permisos: un permiso de crear 3 , un permiso de modicar 4 , y un permiso de eliminar 5 . Lgicamente, asignando permisos a un usuario habilitas que este acceda a realizar la accin que el permiso describe. Nota: El acceso a editar usuarios y permisos tambin es controlado por el sistema de permisos. Si le das a alguien el permiso de editar usuarios, estar en condiciones de editar sus propios permisos, que probablemente no es lo que queras! Tambin puedes asignar usuarios a grupos. Un grupo es simplemente un conjunto de permisos a aplicar a todos los usuarios de ese grupo. Los grupos son tiles para otorgar idnticos permisos a un gran nmero de usuarios.
Estas cuatro lneas de cdigo cambian dramticamente la interfaz de nuestra lista, como se muestra en la gura 6-8.
3 4 5
N. del T.: En el control de seleccin de permisos aparece como Can add N. del T.: Can change N. del T.: Can delete
84
Figura 6-8. Pgina de lista de cambios modicada Cada una de estas lneas indica a la interfaz de administracin que construya diferentes piezas de la interfaz: La opcin list_display controla que columnas aparecen en la tabla de la lista. Por defecto, la lista de cambios muestra una sola columna que contiene la representacin en cadena de caracteres del objeto. Aqu podemos cambiar eso para mostrar el ttulo, el editor y la fecha de publicacin. La opcin list_filter crea una barra de ltrado del lado derecho de la lista. Estaremos habilitados a ltrar por fecha (que te permite ver slo los libros publicados la ltima semana, mes, etc.) y por editor. Puedes indicarle a la interfaz de administracin que ltre por cualquier campo, pero las claves forneas, fechas, booleanos, y campos con un atributo de opciones choices son las que mejor funcionan. Los ltros aparecen cuando tienen al menos 2 valores de dnde elegir. La opcin ordering controla el orden en el que los objetos son presentados en la interfaz de administracin. Es simplemente una lista de campos con los cuales ordenar el resultado; anteponiendo un signo menos a un campo se obtiene el orden reverso. En este ejemplo, ordenamos por fecha de publicacin con los ms recientes al principio. Finalmente, la opcin search_fields crea un campo que permite buscar texto. En nuestro caso, buscar el texto en el campo ttulo (entonces podras ingresar Django para mostrar todos los libros con Django en el ttulo). Usando estas opciones (y las otras descriptas en el captulo 17) puedes, con slo algunas lneas de cdigo, hacer una interfaz de edicin de datos realmente potente y lista para produccin.
85
nos preguntan sobre casos de uso para la interfaz de administracin (Cundo debemos usarlo y por qu?). A lo largo de los aos, hemos descubierto algunos patrones donde pensamos que usar la interfaz de administracin resulta til. Obviamente, es muy til para modicar datos (se vea venir). Si tenemos cualquier tipo de tarea de introduccin de datos, el administrador es difcil de superar. Sospechamos que la gran mayora de lectores de este libro tiene una horda de tareas de este tipo. La interfaz de administracin de Django brilla especialmente cuando usuarios no tcnicos necesitan ser capaces de ingresar datos; ese es el propsito detrs de esta caracterstica, despus de todo. En el peridico donde Django fue creado originalmente, el desarrollo de una caracterstica tpica online un reporte especial sobre la calidad del agua del acueducto municipal, supongamos implicaba algo as: El periodista responsable del artculo se rene con uno de los desarrolladores y discuten sobre la informacin disponible. El desarrollador disea un modelo basado en esta informacin y luego abre la interfaz de administracin para el periodista. Mientras el periodista ingresa datos a Django, el programador puede enfocarse en desarrollar la interfaz accesible pblicamente (la parte divertida!). En otras palabras, la razn de ser de la interfaz de administracin de Django es facilitar el trabajo simultneo de productores de contenido y programadores. Sin embargo, ms all de estas tareas de entrada de datos obvias, encontramos que la interfaz de administracin 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 administracin e ingresar algunos datos de relleno. Esto es usual para encontrar errores de modelado; tener una una interfaz grca al modelo revela problemas rpidamente. Gestin de datos adquiridos: Hay una pequea entrada de datos asociada a un sitio como http://chicagocrime.org, puesto que la mayora de los datos provienen de una fuente automtica. No obstante, cuando surgen problemas con los datos automticos, es til poder entrar y editarlos fcilmente.
7.7 Qu sigue?
Hasta ahora hemos creado algunos modelos y congurado una interfaz de primera clase para modicar datos. En el prximo captulo, nos meteremos en el verdadero guiso del desarrollo Web: creacin y procesamiento de formularios.
7.7. Qu sigue?
87
88
CAPTULO 8
Autor invitado: Simon Willison Si has estado siguiendo el captulo anterior, ya deberas tener un sitio completamente funcional, aunque un poco simple. En este captulo trataremos con la prxima pieza del juego: cmo construir vistas que obtienen entradas desde los usuarios. Comenzaremos haciendo un simple formulario de bsqueda a mano, viendo cmo manejar los datos suministrados al navegador. Y a partir de ah, pasaremos al uso del framework de formularios que trae Django.
8.1 Bsquedas
En la Web todo se trata de bsquedas. Dos de los casos de xito ms grandes, Google y Yahoo, han construido sus empresas multimillonarias alrededor de las bsquedas. Casi todos los sitios observan un gran porcentaje de trco viniendo desde y hacia sus pginas de bsqueda. A menudo, la diferencia entre el xito y el fracaso de un sitio, lo determina la calidad de su bsqueda. As que sera mejor que agreguemos un poco de bsqueda a nuestro pequeo sitio de libros, no? Comenzaremos agregando la vista para la bsqueda a nuestro URLconf (mysite.urls). Recuerda que esto se hace agregando algo como (r^search/$, mysite.books.views.search) al conjunto de URL patterns (patrones). A continuacin, escribiremos la vista search en nuestro mdulo de vistas (mysite.books.views):
from django.db.models import Q from django.shortcuts import render_to_response from models import Book def search(request): query = request.GET.get( q , ) if query: qset = ( Q(title__icontains=query) | Q(authors__first_name__icontains=query) | Q(authors__last_name__icontains=query) ) results = Book.objects.filter(qset).distinct() else: results = [] return render_to_response( " books/search.html " , {
89
Aqu han surgido algunas cosas que todava no hemos vimos. La primera, ese request.GET. As es cmo accedes a los datos del GET desde Django; Los datos del POST se acceden de manera similar, a travs de un objeto llamado request.POST. Estos objetos se comportan exactamente como los diccionarios estndar de Python, y tienen adems otras capacidades, que se cubren en el apndice H. Qu son estos datos del GET y del POST? GET y POST son los dos mtodos que emplean los navegadores para enviar datos a un servidor. Los encontrars 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 /books/search/ empleando el mtodo GET. Hay diferencias de semntica importantes entre el GET y el POST, que no vamos a ver ahora mismo, pero dirjete a http://www.w3.org/2001/tag/doc/whenToUseGet.html si quieres aprender ms. As que la lnea:
query = request.GET.get( q , )
busca un parmetro del GET llamado q y retorna una cadena de texto vaca si este parmetro no fue suministrado. Observa que estamos usando el mtodo get() de request.GET, algo potencialmente confuso. Este mtodo get() es el mismo que posee cualquier diccionario de Python. Lo estamos usando aqu para ser precavidos: no es seguro asumir que request.GET tiene una clave q, as que usamos get(q, ) para proporcionar un valor por omisin, que es (el string vaco). Si hubiramos intentado acceder a la variable simplemente usando request.GET[q], y q no hubiese estado disponible en los datos del GET, se habra lanzado un KeyError. Segundo, qu es ese Q? Los objetos Q se utilizan para ir construyendo consultas complejas en este caso, estamos buscando los libros que coincidan en el ttulo o en el nombre con la consulta. Tcnicamente, estos objetos Q consisten de un QuerySet, y puede leer ms sobre esto en el apndice C. En estas consultas, icontains es una bsqueda en la que no se distinguen maysculas de minsculas (case-insensitive), y que internamente usa el operador LIKE de SQL en la base de datos. Dado que estamos buscando en campos de muchos-a-muchos, es posible que un libro se obtenga ms de una vez (por ej: un libro que tiene dos autores, y los nombres de ambos concuerdan con la consulta). Al agregar .distinct() en el ltrado, se eliminan los resultados duplicados. Todava no hay una plantilla para esta vista. Esto lo solucionar:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.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>
90
{% if results %} <ul> {% for book in results %} <li>{{ book|escape }}</l1> {% endfor %} </ul> {% else %} <p>No books found</p> {% endif %} {% endif %} </body> </html>
A esta altura, lo que esto hace debera ser obvio. Sin embargo, hay unas pocas sutilezas que vale la pena resaltar: action s . en el formulario, esto signica la URL actual. Esta es una buena prctica estndar: no utilices vistas distintas para la pgina que contiene el formulario y para la pgina con los resultados; usa una pgina nica para las dos cosas. Volvemos a insertar el texto de la consulta en el <input>. Esto permite a los usuarios renar fcilmente sus bsquedas sin tener que volver a teclear todo nuevamente. En todo lugar que aparece query y book, lo pasamos por el ltro escape para asegurarnos de que cualquier bsqueda potencialmente maliciosa sea descartada antes de que se inserte en la pgina 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). El Captulo 19 discute XSS y la seguridad con ms detalle. En cambio, no necesitamos preocuparnos por el contenido malicioso en las bsquedas de la base de datos podemos pasar directamente la consulta a la base de datos. 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. Ahora ya tenemos la bsqueda funcionando. Se podra mejorar ms el sitio colocando el formulario de bsqueda en cada pgina (esto es, en la plantilla base). Dejaremos esto de tarea para el hogar. A continuacin veremos un ejemplo ms complejo. Pero antes de hacerlo, discutamos un tpico ms abstracto: el formulario perfecto.
91
de los campos del formulario, reglas de validacin, y una simple plantilla, y Django hace el resto. El resultado es un formulario perfecto que requiere de muy poco esfuerzo.
New Forms? Qu? Cuando Django fue lanzado al pblico por primera vez, posea un sistema de formularios complicado y confuso. Como haca muy dicultosa la produccin de formularios, fue rescrito y ahora se llama newforms (nuevos formularios). Sin embargo, como todava hay cierta cantidad de cdigo que depende del viejo sistema de formularios, Django actualmente viene con ambos paquetes. Al momento de escribir ese libro, el viejo sistema de formularios de Django sigue disponible como django.forms, y el nuevo paquete como django.newforms. En algn momento esto va a cambiar, y django.forms har referencia al nuevo paquete de formularios. Sin embargo, para estar seguros de que los ejemplos de este libro funcionen lo ms ampliamente posible, todos harn referencia a django.newforms. Un formulario de Django es una subclase de django.newforms.Form, tal como un modelo de Django es una subclase de django.db.models.Model. El mdulo django.newforms tambin contiene cierta cantidad de clases Field para los campos. Una lista completa de stas ltimas se encuentra disponible en la documentacin de Django, en http://www.djangoproject.com/documentation/ Nuestro ContactForm consiste de tres campos: un tpico, que se puede elegir entre tres opciones; un mensaje, que es un campo de caracteres; y un emisor, que es un campo de correo electrnico y es opcional (porque incluso los comentarios annimos pueden ser tiles). Hay una cantidad de otros tipos de campos disponibles, y puedes escribir nuevos tipos si ninguno cubre tus necesidades. El objeto formulario sabe cmo hacer una cantidad de cosas tiles por s mismo. Puede validar una coleccin de datos, puede generar sus propios widgets de HTML, puede construir un conjunto de
92
mensajes de error tiles. Y si ests en perezoso, puede incluso dibujar el formulario completo por ti. Incluyamos esto en una vista y vemoslo en accin. En views.py:
from django.db.models import Q from django.shortcuts import render_to_response from models import Book **from forms import ContactForm** def search(request): query = request.GET.get( q , ) if query: qset = ( Q(title__icontains=query) | Q(authors__first_name__icontains=query) | Q(authors__last_name__icontains=query) ) results = Book.objects.filter(qset).distinct() else: results = [] return render_to_response( " books/search.html " , { " results " : results, " query " : query }) **def contact(request):** **form = ContactForm()** **return render_to_response( contact.html , { form : form})**
y en contact.html: La lnea ms interesante aqu es {{ form.as_table }}. form es nuestra instancia de ContactForm, que fue pasada al render_to_response. as_table es un mtodo de ese objeto que reproduce el formulario como una secuencia de renglones de una tabla (tambin pueden usarse as_ul y as_p). El HTML generado se ve as:
<tr> <th><label for= "id_topic" >Topic:</label></th> <td> <select name= "topic" id= "id_topic" > <option value= "general" >General enquiry</option> <option value= "bug" >Bug report</option> <option value= "suggestion" >Suggestion</option> </select> </td> </tr> <tr> <th><label for= "id_message" >Message:</label></th> <td><input type= "text" name= "message" id= "id_message" /></td> </tr> <tr> <th><label for= "id_sender" >Sender:</label></th> <td><input type= "text" name= "sender" id= "id_sender" /></td> </tr>
Observa que las etiquetas <table> y <form> no se han incluido; debes denirlas por tu cuenta en la plantilla. Esto te da control sobre el comportamiento del formulario al ser suministrado. Los elementos label s se incluyen, y proveen a los formularios de accesibilidad desde fbrica. Nuestro formulario actualmente utiliza un widget <input type="text"> para el campo del mensaje. Pero no queremos restringir a nuestros usuarios a una sola lnea de texto, as que la cambiaremos por un widget <textarea>:
93
El framework de formularios divide la lgica de presentacin para cada campo, en un conjunto de widgets. Cada tipo de campo tiene un widget por defecto, pero puedes sobreescribirlo fcilmente, o proporcionar uno nuevo de tu creacin. Por el momento, si se suministra el formulario, no sucede nada. Agreguemos nuestras reglas de validacin:
def contact(request): if request.method == POST : form = ContactForm(request.POST) else: form = ContactForm() return render_to_response( contact.html , { form : form})
Una instancia de formulario puede estar en uno de dos estados: bound (vinculado) o unbound (no vinculado). Una instancia bound se construye con un diccionario (o un objeto que funcione como un diccionario) y sabe cmo validar y volver a representar sus datos. Un formulario unbound no tiene datos asociados y simplemente sabe cmo representarse a s mismo. Intenta hacer clic en Submit en el formulario vaco. La pgina se volver a cargar, mostrando un error de validacin que informa que nuestro campo de mensaje es obligatorio. Intenta tambin ingresar una direccin de correo electrnico invlida. El EmailField sabe cmo validar estas direcciones, por lo menos a un nivel razonable. Cmo especicar datos iniciales Al pasar datos directamente al constructor del formulario, estos se vinculan, y se indica que la validacin debe ser efectuada. A menudo, necesitamos mostrar un formulario inicial con algunos campos previamente rellenados por ejemplo, en un formulario editar. Podemos hacerlo con el argumento de palabras claves initial:
form = CommentForm(initial={ sender : user@example.com })
Si nuestro formulario siempre usar los mismos valores por defecto, podemos congurarlos en la denicin misma del formulario:
message = forms.CharField(widget=forms.Textarea(), **initial= " Replace with your feedback " **)
94
Ahora necesitamos acceder a los datos. Podramos sacarlos directamente del request.POST, pero si lo hiciramos, no nos estaramos beneciando de la conversin de tipos que realiza el framework de formularios. En cambio, usamos form.clean_data:
if form.is_valid(): topic = form.clean_data[ topic ] message = form.clean_data[ message ] sender = form.clean_data.get( sender , noreply@example.com ) # ...
Observa que dado que sender no es obligatorio, proveemos un valor por defecto por si no fue proporcionado. Finalmente, necesitamos registrar los comentarios del usuario. La manera ms fcil de hacerlo es enviando un correo electrnico al administrador del sitio. Podemos hacerlo empleando la funcin:
from django.core.mail import send_mail # ... send_mail( Feedback from your site, topic: message, sender, [ administrator@example.com ] )
%s % topic,
La funcin send_mail tiene cuatro argumentos obligatorios: el asunto y el cuerpo del mensaje, la direccin del emisor, y una lista de direcciones destino. send_mail es un cdigo conveniente que envuelve a la clase EmailMessage de Django. Esta clase provee caractersticas avanzadas como adjuntos, mensajes multiparte, y un control completo sobre los encabezados del mensaje. Una vez enviado el mensaje con los comentarios, redirigiremos a nuestro usuario a una pgina esttica de conrmacin. La funcin de la vista nalizada se ve as:
from from from from django.http import HttpResponseRedirect django.shortcuts import render_to_response django.core.mail import send_mail forms import ContactForm
def contact(request): if request.method == POST : form = ContactForm(request.POST) if form.is_valid(): topic = form.clean_data[ topic ] message = form.clean_data[ message ] sender = form.clean_data.get( sender , noreply@example.com ) send_mail( Feedback from your site, topic: %s % topic, message, sender, [ administrator@example.com ] ) return HttpResponseRedirect( /contact/thanks/ ) else: form = ContactForm() return render_to_response( contact.html , { form : form})
Redirigir luego del POST Si un usuario selecciona actualizar sobre una pgina que muestra una consulta POST, la consulta se repetir. Esto probablemente lleve a un comportamiento no deseado, por ejemplo, que el registro se 8.4. Procesamiento de los datos suministrados 95
agregue dos veces a la base de datos. Redirigir luego del POST es un patrn til que puede ayudar a prevenir este escenario. As que luego de que se haya procesado el POST con xito, redirige al usuario a otra pgina en lugar de retornar HTML directamente.
Este nuevo mtodo ser llamado despus del validador que tiene el campo por defecto (en este caso, el validador de un CharField obligatorio). Dado que los datos del campo ya han sido procesados parcialmente, necesitamos obtenerlos desde el diccionario clean_data del formulario. Usamos una combinacin de len() y split() para contar la cantidad de palabras. Si el usuario ha ingresado muy pocas palabras, lanzamos un error ValidationError. El texto que lleva esta excepcin se mostrar al usuario como un elemento de la lista de errores. Es importante que retornemos explcitamente el valor del campo al nal del mtodo. Esto nos permite modicar el valor (o convertirlo a otro tipo de Python) dentro de nuestro mtodo de validacin. Si nos olvidamos de retornarlo, se retornar None y el valor original ser perdido.
96
campo est disponible como {{ form.fieldname.errors }}. Podemos usar estas variables para construir nuestra propia plantilla para el formulario: {{ form.message.errors }} se muestra como un <ul class=.errorlist"> si se presentan errores y como una cadena de caracteres en blanco si el campo es vlido ( o si el formulario no est vinculado). Tambin podemos tratar a la variable form.message.errors como a un booleano o incluso iterar sobre la misma como en una lista, por ejemplo: En caso de que hubieran errores de validacin, se agrega la clase errors al <div> contenedor y se muestran los errores en una lista ordenada.
PublisherForm es una subclase de Form, tal como la clase ContactForm que creamos manualmente con anterioridad. Podemos usarla de la misma forma:
from forms import PublisherForm def add_publisher(request): if request.method == POST : form = PublisherForm(request.POST) if form.is_valid(): form.save() return HttpResponseRedirect( /add_publisher/thanks/ ) else: form = PublisherForm() return render_to_response( books/add_publisher.html , { form : form})
El archivo add_publisher.html es casi idntico a nuestra plantilla contact.html original, as que la omitimos. Recuerda adems agregar un nuevo patrn al URLconf: (r^add_publisher/$, mysite.books.views.add_publisher). Ah se muestra un atajo ms. Dado que los formularios derivados de modelos se emplean a menudo para guardar nuevas instancias del modelo en la base de datos, la clase del formulario creada por form_for_model incluye un conveniente mtodo save(). Este mtodo trata con el uso comn; pero puedes ignorarlo si deseas hacer algo ms que tenga que ver con los datos suministrados. form_for_instance() es un mtodo que est relacionado con el anterior, y puede crear formularios preinicializados a partir de la instancia de un modelo. Esto es til al crear formularios editar.
97
8.8 Qu sigue?
Este captulo concluye con el material introductorio de este libro. Los prximos trece captulos tratan con varios tpicos avanzados, incluyendo la generacin de contenido que no es HTML (Captulo 11), seguridad (Captulo19), y entrega del servicio (Captulo 20). Luego de estos primeros siete captulos, deberas saber lo suciente como para comenzar a escribir tus propios proyectos en Django. El resto del material de este libro te ayudar a completar las piezas faltantes a medida que las vayas necesitando. Comenzaremos el Captulo 8 yendo hacia atrs, volviendo para darle una mirada ms de cerca a las vistas y a los URLconfs (introducidos por primera vez en el Captulo 3).
98
CAPTULO 9
En el Captulo 3, explicamos las bases de las funciones vista de Django y las URLconfs. Este captulo entra en detalle sobre funcionalidad avanzada en esas dos partes del framework.
Como se explic en el Captulo 3, cada entrada de la URLconf incluye su funcin vista asociada, que se pasa directamente como un mtodo. Esto signica que es necesario importar las funciones view en la parte superior del mdulo. Pero a medida que las aplicaciones Django crecen en complejidad, sus URLconf crecen tambin, y mantener esos import puede ser tedioso de manejar. (Por cada nueva funcin vista, tienes que recordar importarla y la declaracin de importaciones tiende a volverse demasiado larga si se utiliza este mtodo). Es posible evitar esa tarea tediosa importando el mdulo views directamente. Este ejemplo de URLconf es equivalente al anterior:
from django.conf.urls.defaults import * from mysite import views
99
urlpatterns = patterns( , ( r ^now/$ , views.current_datetime), ( r ^now/plus( \ d{1,2})hours/$ , views.hours_ahead), ( r ^now/minus( \ d{1,2})hours/$ , views.hours_behind), ( r ^now/in_chicago/$ , views.now_in_chicago), ( r ^now/in_london/$ , views.now_in_london), )
Django ofrece otra forma de especicar la funcin vista para un patrn en particular en la URLconf: se le puede pasar un string que contiene el nombre del mdulo y de la funcin en lugar del mtodo. Continuando con el ejemplo:
from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^now/$ , mysite.views.current_datetime ), ( r ^now/plus( \ d{1,2})hours/$ , mysite.views.hours_ahead ), ( r ^now/minus( \ d{1,2})hours/$ , mysite.views.hours_behind ), ( r ^now/in_chicago/$ , mysite.views.now_in_chicago ), ( r ^now/in_london/$ , mysite.views.now_in_london ), )
(Nota que los nombres de las vistas estn do mysite.views.current_datetime con mysite.views.current_datetime.)
Al usar esta tcnica ya no es necesario importar las funciones vista; Django importa automticamente la funcin vista apropiada la primera vez que sea necesaria, segn el string que describe el nombre y la ruta de la funcin vista. Otro atajo que puedes tomar al usar la tcnica del string es sacar factor comn de prejos view. En nuestro ejemplo URLconf, cada uno de los strings vista comienza con mysite.views, lo cual es redundante. Podemos factorizar ese prejo comn y pasarlo como primer argumento de patterns(), as:
from django.conf.urls.defaults import * urlpatterns = patterns( mysite.views , ( r ^now/$ , current_datetime ), ( r ^now/plus( \ d{1,2})hours/$ , hours_ahead ), ( r ^now/minus( \ d{1,2})hours/$ , hours_behind ), ( r ^now/in_chicago/$ , now_in_chicago ), ( r ^now/in_london/$ , now_in_london ), )
Nota que no se pone un punto detrs del prejo, ni un punto delante de los string vista. Django los pone automticamente. Con estos dos enfoques en mente, cul es mejor? Realmente depende de tu estilo personal de programacin y tus necesidades. Las siguientes son ventajas del enfoque string: Es ms compacto, porque no requiere que importes las funciones vista. Resulta en URLconfs ms fciles de leer y de manejar si tus funciones vista estn extendidas por varios mdulos Python diferentes. Las siguientes son ventajas del enfoque del mtodo:
100
Permite un fcil empaquetado de funciones vista. Ver la seccin Empaquetado de funciones vista ms adelante en este captulo. Es ms Pythnico es decir, est ms en lnea con las tradiciones Python, como la de pasar funciones como objetos. Ambos enfoques son vlidos e incluso puedes mezclarlos dentro de la misma URLconf. La eleccin es tuya.
Despus:
from django.conf.urls.defaults import * urlpatterns = patterns( mysite.views , ( r ^/?$ , archive_index ), ( r ^( \ d{4})/([a-z]{3})/$ , archive_month ), ) urlpatterns += patterns( weblog.views , ( r ^tag/( \ w+)/$ , tag ), )
Lo nico que le importa al framework es que existe una variable a nivel mdulo llamada urlpatterns. Esta variable puede ser construida de forma dinmica, como lo hacemos en este ejemplo.
101
( r ^debuginfo/$ , mysite.views.debug ), )
En este ejemplo, la URL /debuginfo/ slo estar disponible si tu conguracin DEBUG tiene el valor True.
Para llamarla con argumentos posicionales, se especican los argumentos en el orden en que estn listados en la denicin de la funcin:
sell( Socks , $2.50 , 6)
Para llamarla con argumentos de palabra clave, se especican los nombres de los argumentos junto con sus valores. Las siguientes sentencias son equivalentes:
sell(item= Socks , price= $2.50 , quantity=6) sell(item= Socks , quantity=6, price= $2.50 ) sell(price= $2.50 , item= Socks , quantity=6) sell(price= $2.50 , quantity=6, item= Socks ) sell(quantity=6, item= Socks , price= $2.50 ) sell(quantity=6, price= $2.50 , item= Socks )
Finalmente, se pueden mezclar los argumentos posicionales y por palabra clave, siempre y cuando los argumentos posicionales estn listados antes que los argumentos por palabra clave. Las siguientes sentencias son equivalentes a los ejemplos anteriores:
sell( Socks , $2.50 , quantity=6) sell( Socks , price= $2.50 , quantity=6) sell( Socks , quantity=6, price= $2.50 )
En las expresiones regulares de Python, la sintaxis para los grupos de expresiones regulares con nombre es (?P<nombre>patrn), donde nombre es el nombre del grupo y patrn es algn patrn a buscar. Aqu hay un ejemplo de URLconf que usa grupos sin nombre:
102
from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns( , ( r ^articles/( \ d{4})/$ , views.year_archive), ( r ^articles/( \ d{4})/( \ d{2})/$ , views.month_archive), )
Aqu est la misma URLconf, reescrita para usar grupos con nombre:
from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns( , ( r ^articles/(?P<year> \ d{4})/$ , views.year_archive), ( r ^articles/(?P<year> \ d{4})/(?P<month> \ d{2})/$ , views.month_archive), )
Esto produce exactamente el mismo resultado que el ejemplo anterior, con una sutil diferencia: se le pasa a las funciones vista los valores capturados como argumentos clave en lugar de argumentos posicionales. Por ejemplo, con los grupos sin nombre una peticin a /articles/2006/03/ resultara en una llamada de funcin equivalente a esto:
month_archive(request, 2006 , 03 )
Sin embargo, con los grupos con nombre, la misma peticin resultara en esta llamada de funcin:
month_archive(request, year= 2006 , month= 03 )
En la prctica, usar grupos con nombres hace que tus URLconfs sean un poco ms explcitas y menos propensas a bugs causados por argumentos y puedes reordenar los argumentos en las deniciones de tus funciones vista. Siguiendo con el ejemplo anterior, si quisiramos cambiar las URLs para incluir el mes antes del ao, y estuviramos usando grupos sin nombre, tendramos que acordarnos de cambiar el orden de los argumentos en la vista month_archive. Si estuviramos usando grupos con nombre, cambiar el orden de los parmetros capturados en la URL no tendra ningn efecto sobre la vista. Por supuesto, los benecios de los grupos con nombre tienen el costo de la falta de brevedad; algunos desarrolladores opinan que la sintaxis de los grupos con nombre es fea y larga. An as, otra ventaja de los grupos con nombres es la facilidad de lectura, especialmente para las personas que no estn ntimamente relacionadas con las expresiones regulares o con tu aplicacin Django en particular. Es ms fcil ver lo que est pasando, a primera vista, en una URLconf que usa grupos con nombre.
103
Con este cdigo nos estamos repitiendo y eso no es elegante. Al comienzo, podras pensar en reducir la redundancia usando la misma vista para ambas URLs, poniendo parntesis alrededor de la URL para capturarla y comprobando la URL dentro de la vista para determinar la plantilla, como mostramos a continuacin:
# urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns( , ( r ^(foo)/$ , views.foobar_view), ( r ^(bar)/$ , views.foobar_view), ) # views.py from django.shortcuts import render_to_response from mysite.models import MyModel def foobar_view(request, url): m_list = MyModel.objects.filter(is_new=True) if url == foo : template_name = template1.html elif url == bar : template_name = template2.html return render_to_response(template_name, { m_list : m_list})
Sin embargo, el problema con esa solucin es que acopla fuertemente tus URLs y tu cdigo Si decides renombrar /foo/ a /fooey/, tienes que recordar cambiar el cdigo de la vista. La solucin elegante involucra un parmetro URLconf opcional. Cada patrn en una URLconf puede
104
incluir un tercer tem: un diccionario de argumentos de palabra clave para pasarle a la funcin vista. Con esto en mente podemos reescribir nuestro ejemplo anterior as:
# urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns( , ( r ^foo/$ , views.foobar_view, { template_name : template1.html }), ( r ^bar/$ , views.foobar_view, { template_name : template2.html }), ) # views.py from django.shortcuts import render_to_response from mysite.models import MyModel def foobar_view(request, template_name): m_list = MyModel.objects.filter(is_new=True) return render_to_response(template_name, { m_list : m_list})
Como puedes ver, la URLconf en este ejemplo especica template_name en la URLconf. La funcin vista lo trata como a cualquier otro parmetro. Esta tcnica de la opcin extra en la URLconf es una linda forma de enviar informacin adicional a tus funciones vista sin tanta complicacin. Por ese motivo es que es usada por algunas aplicaciones incluidas en Django, ms notablemente el sistema de vistas genricas, que tratamos en el ::docCaptulo 9<chapter09>. La siguiente seccin contiene algunas ideas sobre cmo puedes usar la tcnica de la opcin extra en la URLconf como parte de tus proyectos. Simulando valores capturados en URLconf Supongamos que posees un conjunto de vistas que son disparadas va un patrn y otra URL que no lo es pero cuya lgica de vista es la misma. 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. Por ejemplo, podras tener una aplicacin que muestra algunos datos para un da particular, con URLs tales como:
/mydata/jan/01/ /mydata/jan/02/ /mydata/jan/03/ # ... /mydata/dec/30/ /mydata/dec/31/
Esto es lo sucientemente simple de manejar puedes capturar los mismos en una URLconf como esta (usando sintaxis de grupos con nombre):
urlpatterns = patterns( , ( r ^mydata/(?P<month> \ w{3})/(?P<day> \ d \ d)/$ , views.my_view), )
105
Este enfoque es simple y directo no es nada que no hayamos visto antes. El truco entra en juego cuando quieres agregar otra URL que usa my_view pero cuya URL no incluye un month ni/o un day. Por ejemplo, podras querer agregar otra URL, /mydata/birthday/, que sera equivalente a /mydata/jan/06/. Puedes sacar provecho de opciones extra de las URLconf de la siguiente forma:
urlpatterns = patterns( , ( r ^mydata/birthday/$ , views.my_view, { month : jan , day : 06 }), ( r ^mydata/(?P<month> \ w{3})/(?P<day> \ d \ d)/$ , views.my_view), )
El detalle genial aqu es que no necesitas cambiar tu funcin vista para nada. A la funcin vista slo le incumbe el obtener los parmetros month y day no importa si los mismos provienen de la captura de la URL o de parmetros extra. Convirtiendo una vista en genrica Es una buena prctica de programacin el factorizar para aislar las partes comunes del cdigo. Por ejemplo, con estas dos funciones Python:
def say_hello(person_name): print Hello, %s % person_name def say_goodbye(person_name): print Goodbye, %s % person_name
Puedes aplicar la misma losofa a tus vistas Django usando los parmetros extra de URLconf. Con esto en mente, puedes comenzar a hacer abstracciones de nivel ms alto de tus vistas. En lugar de pensar Esta vista muestra una lista de objetos Event y Esta otra vista muestra una lista de objetos BlogEntry, descubre que ambas son casos especcos de Una vista que muestra una lista de objetos, donde el tipo de objeto es variable. Usemos este cdigo como ejemplo:
# urls.py from django.conf.urls.defaults import * from mysite import views urlpatterns = patterns( , ( r ^events/$ , views.event_list), ( r ^blog/entries/$ , views.entry_list), ) # views.py from django.shortcuts import render_to_response from mysite.models import Event, BlogEntry def event_list(request): obj_list = Event.objects.all() return render_to_response( mysite/event_list.html , { event_list : obj_list}) def entry_list(request): obj_list = BlogEntry.objects.all() return render_to_response( mysite/blogentry_list.html , { entry_list : obj_list})
106
Ambas vistas hacen esencialmente lo mismo: muestran una lista de objetos. Refactoricemos el cdigo para extraer el tipo de objetos que muestran:
# urls.py from django.conf.urls.defaults import * from mysite import models, views urlpatterns = patterns( , ( r ^events/$ , views.object_list, { model : models.Event}), ( r ^blog/entries/$ , views.object_list, { model : models.BlogEntry}), ) # views.py from django.shortcuts import render_to_response def object_list(request, model): obj_list = model.objects.all() template_name = mysite/ %s _list.html % model.__name__.lower() return render_to_response(template_name, { object_list : obj_list})
Con esos pequeos cambios tenemos, de repente, una vista reusable e independiente del modelo. De ahora en adelante, cada vez que necesitemos una lista que muestre una listado de objetos, podemos simplemente reusar esta vista object_list en lugar de escribir cdigo de vista. A continuacin, un par de notas acerca de lo que hicimos: Estamos pasando las clases de modelos directamente, como el parmetro model. El diccionario de opciones extra de ULconf puede pasar cualquier tipo de objetos Python no slo strings. La lnea model.objects.all() es un ejemplo de tipado de pato (duck typing): Si camina como un pato, y habla como un pato, podemos tratarlo como un pato. Nota que el cdigo no conoce de qu tipo de objeto se trata model; el nico requerimiento es que model tenga un atributo objects, el cual a su vez tiene un mtodo all(). Estamos usando model.__name__.lower() para determinar el nombre de la plantilla. Cada clase Python tiene un atributo __name__ que retorna el nombre de la clase. Esta caracterstica es til en momentos como este, cuando no conocemos el tipo de clase hasta el momento de la ejecucin. Por ejemplo, el __name__ de la clase BlogEntry es la cadena BlogEntry. En una sutil diferencia entre este ejemplo y el ejemplo previo, estamos pasando a la plantilla el nombre de variable genrico object_list. Podemos fcilmente cambiar este nombre de variable a blogentry_list o event_list, pero hemos dejado eso como un ejercicio para el lector. Debido a que los sitios Web impulsados por bases de datos tienen varios patrones comunes, Django incluye un conjunto de vistas genricas que usan justamente esta tcnica para ahorrarte tiempo. Nos ocupamos de las vistas genricas incluidas con Django en el prximo captulo. Pasando opciones de conguracin a una vista Si ests distribuyendo una aplicacin Django, es probable que tus usuarios deseen cierto grado de conguracin. En este caso, es una buena idea agregar puntos de extensin a tus vistas para las opciones de conguracin que piensas que la gente pudiera desear cambiar. Puedes usar los parmetros extra de URLconf para este n. Una parte de una aplicacin que normalmente se hace congurable es el nombre de la plantilla:
def my_view(request, template_name): var = do_something() return render_to_response(template_name, { var : var})
107
Entendiendo la precedencia entre valores captuardos vs. opciones extra Cuando se presenta un conicto, los parmetros extra de la URLconf tiene precedencia sobre los parmetros capturados. En otras palabras, si tu URLconf captura una variable de grupo con nombre y un parmetro extra de URLconf incluye una variable con el mismo nombre, se usar el parmetro extra de la URLconf. Por ejemplo, analicemos esta URLconf:
from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^mydata/(?P<id> \ d+)/$ , views.my_view, { id : 3}), )
Aqu, tanto la expresin regular como el diccionario extra incluye un id. Tiene precedencia el id jo especicado. Esto signica que cualquier peticin (por ej. /mydata/2/ o /mydata/432432/) sern tratados como si id estuviera jado a 3, independientemente del valor capturado en la URL. Los lectores atentos notarn que en este caso es una prdida de tiempo y de tipeo capturar id en la expresin regular, porque su valor ser siempre descartado en favor del valor proveniente del diccionario. Esto es correcto; lo traemos a colacin slo para ayudarte a evitar el cometer ese error.
Aqu, ambos patrones de URL apuntan a la misma vista views.page pero el primer patrn no captura nada de la URL. Si el primer patrn es disparado, la funcin page() usar su argumento por omisin para num, "1". Si el segundo patrn es disparado, page() usar el valor de num que se haya capturado mediante la expresin regular. Es comn usar esta tcnica en combinacin con opciones de conguracin, como explicamos previamente. Este ejemplo implementa una pequea mejora al ejemplo de la seccin Pasando opciones de conguracin a una vista: provee un valor por omisin para template_name:
def my_view(request, template_name= mysite/my_view.html ): var = do_something() return render_to_response(template_name, { var : var})
108
Esto se disparar con URLs como /myblog/entries/add/ y /auth/groups/add/. Sin embargo, la pgina agregar de un objeto usuario (/auth/user/add/) es un caso especial la misma no muestra todos los campos del formulario, muestra dos campos de contrasea, etc. Podramos resolver este problema tratando esto como un caso especial en la vista, de esta manera:
def add_stage(request, app_label, model_name): if app_label == auth and model_name == user: # do special-case code else: # do normal code
pero eso es poco elegante por una razn que hemos mencionado en mltiples oportunidades en este captulo: Coloca lgica de URLs en la vista. Una manera ms elegante sera la de hacer uso del hecho que las URLconfs se procesan desde arriba hacia abajo:
urlpatterns = patterns( , # ... ( ^auth/user/add/$ , django.contrib.admin.views.auth.user_add_stage ), ( ^([^/]+)/([^/]+)/add/$ , django.contrib.admin.views.main.add_stage ), # ... )
Con esto, una peticin de /auth/user/add/ ser manejada por la vista user_add_stage. Aunque dicha URL coincide con el segundo patrn, coincide primero con el patrn ubicado ms arriba. (Esto es lgica de corto circuito).
el argumento year de views.year.archive() ser una cadena, no un entero, aun cuando \d{4} slo coincidir con cadenas que representen enteros. Es importante tener esto presente cuando ests escribiendo cdigo de vistas. Muchas funciones incluidas con Python son exigentes (y eso es bueno) acerca de aceptar objetos de cierto tipo. Un error comn es intentar crear un objeto datetime.date con valores de cadena en lugar de valores enteros:
>>> import datetime >>> datetime.date( 1993 , 7 , 9 ) Traceback (most recent call last): ... TypeError: an integer is required >>> datetime.date(1993, 7, 9) datetime.date(1993, 7, 9)
109
Notar que int() lanza un ValueError cuando le pasas una cadena que no est compuesta nicamente de dgitos, pero estamos evitando ese error en este caso porque la expresin regular en nuestra URLconf ya se ha asegurado que slo se pasen a la funcin vista cadenas que contengan dgitos.
Existe aqu un detalle importante: en este ejemplo, la expresin regular que apunta a un include() no tiene un $ (carcter que coincide con un n de cadena) pero si incluye una barra al nal. Cuando Django 110 Captulo 9. Captulo 8: Vistas avanzadas y URLconfs
encuentra include(), elimina todo el fragmento de la URL que ya ha coincidido hasta ese momento y enva la cadena restante a la URLconf incluida para su procesamiento subsecuente. Continuando con este ejemplo, esta es la URLconf mysite.blog.urls:
from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^( \ d \ d \ d \ d)/$ , mysite.blog.views.year_detail ), ( r ^( \ d \ d \ d \ d)/( \ d \ d)/$ , mysite.blog.views.month_detail ), )
Con esas dos URLconfs, veremos aqu cmo seran manejadas algunas peticiones de ejemplo: /weblog/2007/: En la primera URLconf, el patrn r^weblog/ coincide. Debido a que es un include(), Django quita todo el texto coincidente, que en este caso es weblog/. La parte restante de la URL es 2007/, la cual coincide con la primera lnea en la URLconf mysite.blog.urls. /weblog//2007/: En la primera URLconf, el patrn r^weblog/ coincide. Debido a que es un include(), Django quita todo el texto coinciente, que en este caso es weblog/. La parte restante de la URL es /2007/ (con una barra inicial), la cual no coincide con ninguna de la lneas en la URLconf mysite.blog.urls. /about/: Esto coincide con el patrn de la vista mysite.views.about en la primera URLconf, demostrando que puedes combinar patrones include() con patrones no include().
En este ejemplo, la variable capturada username() es pasada a la URLconf incluida y, por lo tanto, a todas las funciones vista en dicha URLconf. Notar que los parmetros capturados sern pasados siempre a todas las lneas en la URLconf incluida, con independencia de si la vista de la lnea realmente acepta esos parmetros como vlidos. Por esta razn esta tcnica solamente es til si ests seguro de que cada vista en la URLconf incluida acepta los parmetros que ests pasando.
extra sern pasadas a todas las lneas en la URLconf incluida. Por ejemplo, los siguientes dos conjuntos de URLconfs son funcionalmente idnticos. Conjunto uno:
# urls.py from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^blog/ , include( inner ), { blogid : 3}), ) # inner.py from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^archive/$ , mysite.views.archive ), ( r ^about/$ , mysite.views.about ), ( r ^rss/$ , mysite.views.rss ), )
Conjunto dos:
# urls.py from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^blog/ , include( inner )), ) # inner.py from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^archive/$ , mysite.views.archive , { blogid : 3}), ( r ^about/$ , mysite.views.about , { blogid : 3}), ( r ^rss/$ , mysite.views.rss , { blogid : 3}), )
Como en el caso de los parmetros capturados (sobre los cuales se explic en la seccin anterior), las opciones extra se pasarn siempre a todas las lneas en la URLconf incluida, sin importar de si la vista de la lnea realmente acepta esas opciones como vlidas. Por eta razn esta tcnica es til slo si ests seguro que todas las vistas en la URLconf incluida acepta las opciones extra que ests pasando.
9.3 Qu sigue?
Uno de los principales objetivos de Django es reducir la cantidad de cdigo que los desarrolladores deben escribir y en este captulo hemos sugerido formas en las cuales se puede reducir el cdigo de tus vistas y URLconfs. El prximo paso lgico en la reduccin de cdigo es eliminar completamente la necesidad de escribir vistas. Ese es el tpico del prximo captulo.
112
CAPTULO 10
De nuevo aparece aqu un tema recurrente en este libro: en el peor de los casos, el desarrollo Web es aburrido y montono. Hasta aqu, hemos cubierto cmo Django trata de alejar parte de esa monotona en las capas del modelo y las plantillas, pero los desarrolladores Web tambin experimentan este aburrimiento al nivel de las vistas. Las vistas genricas de Django fueron desarrolladas para aliviar ese dolor. stas recogen ciertos estilos y patrones comunes encontrados en el desarrollo de vistas y los abstraen, de modo que puedas escribir rpidamente vistas comunes de datos sin que tengas que escribir mucho cdigo. De hecho, casi todos los ejemplos de vistas en los captulos precedentes pueden ser reescritos con la ayuda de vistas genricas. El Captulo 8 reri brevemente sobre cmo haras para crear una vista genrica. Para repasar, podemos reconocer ciertas tareas comunes, como mostrar una lista de objetos, y escribir cdigo que muestra una lista de cualquier objeto. Por lo tanto el modelo en cuestin puede ser pasado como un argumento extra a la URLconf. Django viene con vistas genricas para hacer lo siguiente: Realizar tareas sencillas comunes: redirigir a una pgina diferente y renderizar una plantilla dada. Mostrar pginas de listado y detalle para un solo objeto. Las vistas event_list y entry_list del Captulo 8 son ejemplos de vistas de listado. Una pgina de evento simple es un ejemplo de lo que llamamos vista detallada. Presentar objetos basados en fechas en pginas de archivo de tipo da/mes/ao, su detalle asociado, y las pginas ms recientes. Los archivos por da, mes, ao del Weblog de Django (http://www.djangoproject.com/weblog/) estn construidos con ellas, como lo estaran los tpicos archivos de un peridico. Permitir a los usuarios crear, actualizar y borrar objetos con o sin autorizacin. Agrupadas, estas vistas proveen interfaces fciles para realizar las tareas ms comunes que encuentran los desarrolladores.
113
from django.conf.urls.defaults import * from django.views.generic.simple import direct_to_template urlpatterns = patterns( , ( ^about/$ , direct_to_template, { template : about.html }) )
Aunque esto podra verse un poco mgico a primera vista mira, una vista sin cdigo! , es en realidad exactamente lo mismo que los ejemplos en el Captulo 8: la vista direct_to_template simplemente toma informacin del diccionario de parmetros extra y usa esa informacin cuando renderiza la vista. Ya que esta vista genrica y todas las otras es una funcin de vista regular como cualquier otra, podemos reusarla dentro de nuestras propias vistas. Como ejemplo, extendamos nuestro ejemplo about para mapear URLs de la forma /about/<cualquiercosa>/ para renderizar estticamente /about/<cualquiercosa>.html. Haremos esto primero modicando la URLconf para que apunte a una funcin de vista: A continuacin, escribimos la vista about_pages:
from django.http import Http404 from django.template import TemplateDoesNotExist from django.views.generic.simple import direct_to_template def about_pages(request, page): try: return direct_to_template(request, template= " about/ %s .html " % page) except TemplateDoesNotExist: raise Http404()
Aqu estamos tratando direct_to_template como cualquier otra funcin. Ya que esta devuelve una HttpResponse, podemos retornarlo as como est. La nica ligera dicultad aqu es ocuparse de las plantillas perdidas. No queremos que una plantilla inexistente cause un error de servidor, por lo tanto atrapamos las excepciones TemplateDoesNotExist y en su lugar devolvemos errores 404. 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.html" % page). A primera vista, esto parece como una clsica vulnerabilidad de recorrido de directorio 1 (discutida en detalle en el Captulo 19). Pero es realmente una vulnerabilidad? No exactamente. S, un valor creado maliciosamente de page podra causar un recorrido de directorio, pero aunque page es tomado de la URL solicitada, no todos los valores sern aceptados. La clave esta en la URLconf: estamos usando la expresin regular \w+ para vericar la parte page de la URL y \w slo acepta letras y nmeros. Por lo tanto, cualquier caracter malicioso (puntos y barras, en este caso) sern rechazadas por el sistema de resolucin de URLs antes de alcanzar la vista en s.
114
Demos un vistazo a una de estas vistas genricas: la vista object list. Usaremos el objeto Publisher del Captulo 5:
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 __unicode__(self): return self.name class Meta: ordering = [ " -name " ] class Admin: pass
Para construir una pgina listado de todos los books, usaremos la URLconf bajo estas lneas:
from django.conf.urls.defaults import * from django.views.generic import list_detail from mysite.books.models import Publisher publisher_info = { " queryset " : Publisher.objects.all(), } urlpatterns = patterns( , ( r ^publishers/$ , list_detail.object_list, publisher_info) )
Ese es todo el cdigo Python que necesitamos escribir. Sin embargo, todava necesitamos escribir una plantilla. Podramos decirle explcitamente a la vista object_list que plantilla debe usar incluyendo una clave template_name en el diccionario de argumentos extra, pero en la ausencia de una plantilla explcita Django inferir una del nombre del objeto. En este caso, la plantilla inferida ser "books/publisher_list.html" la parte books proviene del nombre de la aplicacin que dene el modelo, mientras que la parte publisher es slo la versin en minsculas del nombre del modelo. Esta plantilla ser renderizada en un contexto que contiene una variable llamada object_list la cual contiene todos los objetos book. Una plantilla muy simple podra verse como la siguiente:
{% extends "base.html" %} {% block content %} <h2>Publishers</h2> <ul> {% for publisher in object_list %} <li>{{ publisher.name }}</li> {% endfor %} </ul> {% endblock %}
Eso es realmente todo en lo referente al tema. Todas las geniales caractersticas de las vistas genricas provienen de cambiar el diccionario info pasado a la vista genrica. El Apndice D documenta todas las vistas genricas y todas sus opciones en detalle; el resto de este captulo considerar algunas de las maneras comunes en que t puedes personalizar y extender las vistas genricas.
115
Proveer un template_object_name til es siempre una buena idea. Tus compaeros de trabajo que disean las plantillas te lo agradecern.
Esto llenara una variable {{ book_list }} en el contexto de la plantilla. Este patrn puede ser usado para pasar cualquier informacin hacia la plantilla para la vista genrica. Es muy prctico. Sin embargo, en realidad hay un error sutil aqu puedes detectarlo? El problema aparece cuando las consultas en extra_context son evaluadas. Debido a que este ejemplo coloca Publisher.objects.all() en la URLconf, slo se evaluar una vez (cuando la URLconf se cargue por primera vez). Una vez que agregues o elimines publishers, notars que la vista genrica
116
no reeja estos cambios hasta que reinicias el servidor Web (mira Almacenamiento en cach y QuerySets en el Apndice C para mayor informacin sobre cundo los QuerySets son almacenados en la cache y evaluados). Este problema no se aplica al argumento queryset de las vistas genricas. Ya que Django sabe que ese QuerySet en particular nunca debe ser almacenado en la cach, la vista genrica se hace cargo de limpiar la cach cuando cada vista es renderizada. La solucin es usar un callback 2 en extra_context en vez de un valor. Cualquier callable 3 (por ejemplo, una funcin) que sea pasado a extra_context ser evaluado cuando su vista sea renderizada (en vez de slo la primera vez). Puedes hacer esto con una funcin explcitamente denida:
def get_books(): return Book.objects.all() publisher_info = { " queryset " : Publisher.objects.all(), " template_object_name " : " publisher " , " extra_context " : **{ " book_list " : get_books}** }
o puedes usar una versin menos obvia pero ms corta que se basa en el hecho de que Publisher.objects.all es en s un callable:
publisher_info = {p " queryset " : Publisher.objects.all(), " template_object_name " : " publisher " , " extra_context " : **{ " book_list " : Book.objects.all}** }
Nota la falta de parntesis despus de Book.objects.all; esto hace referencia a la funcin sin invocarla realmente (cosa que har la vista genrica luego).
Este es un muy lindo y simple ejemplo, pero ilustra bien la idea. Por supuesto, t usualmente querrs hacer ms que slo reordenar objetos. Si quieres presentar una lista de books de un publisher particular, puedes usar la misma tcnica:
**apress_books = {** ** " queryset " : Book.objects.filter(publisher__name= " Apress Publishing " ),**
2 3
N. del T.: llamada a funcin. N. del T.: en Python cualquier objeto puede ser llamado como funcin.
117
** " template_name " : " books/apress_list.html " ** **}** urlpatterns = patterns( , ( r ^publishers/$ , list_detail.object_list, publisher_info), **( r ^books/apress/$ , list_detail.object_list, apress_books),** )
Nota que adems de un queryset ltrado, tambin estamos usando un nombre de plantilla personalizado. Si no lo hiciramos, la vista genrica usara la misma plantilla que la lista de objetos genrica 4 , que puede no ser lo que queremos. Tambin nota que sta no es una forma muy elegante de hacer publisher-specic books. Si queremos agregar otra pgina publisher, necesitamos otro puado de lneas en la URLconf, y ms de unos pocos publishers no ser razonable. Enfrentaremos este problema en la siguiente seccin. Si obtienes un error 404 cuando solicitas /books/apress/, para estar seguro, verica que en realidad tienes un Publisher con el nombre Apress Publishing. Las vistas genricas tienen un parmetro allow_empty para este caso. Mira el Apndice D para mayores detalles.
N. del T.: vanilla object list. N. del T.: hard-coded. N. del T.: wrap.
118
Esto funciona porque en realidad no hay nada en especial sobre las vistas genricas son slo funciones Python. Como cualquier funcin de vista, las vistas genricas esperan un cierto conjunto de argumentos y retornan objetos HttpResponse. Por lo tanto, es increblemente fcil encapsular una pequea funcin sobre una vista genrica que realiza trabajo adicional antes (o despus; mira la siguiente seccin) de pasarle el control a la vista genrica. Nota: Nota que en el ejemplo anterior pasamos el publisher que se est mostrando actualmente en el extra_context. Esto es usualmente una buena idea en wrappers de esta naturaleza; le permite a la plantilla saber qu objeto padre esta siendo navegado en ese momento.
Podemos usar un mtodo similar para alterar la respuesta devuelta por la vista genrica. Si quisiramos proporcionar una versin en texto plano 7 que se pueda descargar desde la lista de autores, podramos usar una vista como esta:
7
119
def author_list_plaintext(request): response = list_detail.object_list( request, queryset = Author.objects.all(), mimetype = " text/plain " , template_name = " books/author_list.txt " ) response[ " Content-Disposition " ] = " attachment; filename=authors.txt " return response
Esto funciona porque la vista genrica devuelve simplemente objetos HttpResponse que pueden ser tratados como diccionarios para establecer las cabeceras HTTP. Este arreglo de Content-Disposition, por otro lado, instruye al navegador a descargar y guardar la pgina en vez de mostrarla en pantalla.
10.4 Qu sigue?
En este captulo hemos examinado slo un par de las vistas genricas que incluye Django, pero las ideas generales presentadas aqu deberan aplicarse a cualquier vista genrica. El Apndice D cubre todas las vistas disponibles en detalle, y es de lectura obligada si quieres sacar el mayor provecho de esta caracterstica. En el prximo captulo ahondamos profundamente en el funcionamiento interno de las plantillas de Django, mostrando todas las maneras geniales en que pueden ser extendidas. Hasta ahora, hemos tratado el sistema de plantillas meramente como una herramienta esttica que puedes usar para renderizar tu contenido.
120
CAPTULO 11
Aunque la mayor parte de tu interaccin con el sistema de plantillas (templates) de Django ser en el rol de autor, probablemente quieras algunas veces modicar y extender el sistema de plantillas as sea para agregar funcionalidad, o para hacer tu trabajo ms fcil de alguna otra manera. Este captulo se adentra en el sistema de plantillas de Django, cubriendo todo lo que necesitas saber, ya sea por si planeas extender el sistema, o por si slo eres curioso acerca de su funcionamiento. Si ests tratando de utilizar el sistema de plantillas de Django como parte de otra aplicacin, es decir, sin el resto del framework, lee la seccin Congurando el Sistema de plantillas en modo autnomo_ luego en este mismo captulo.
Una variable es un smbolo dentro de una plantilla que emite un valor. Las etiquetas de variable deben ser rodeadas por {{ y }}: My rst name is {{ rst_name }}. My last name is {{ last_name }}. Un contexto es un mapeo entre nombres y valores (similar a un diccionario de Python) que es pasado a una plantilla.
121
Una plantilla renderiza un contexto reemplazando los huecos que dejan las variables por valores tomados del contexto y ejecutando todas las etiquetes de bloque. Para ms detalles acerca de estos trminos, referirse al Captulo 4. El resto de este captulo discute las distintas maneras de extender el sistema de plantillas. Aunque primero, debemos dar una mirada a algunos conceptos internos que quedaron fuera del Captulo 4 por simplicidad.
122
app : My app , user : request.user, ip_address : request.META[ REMOTE_ADDR ], message : I am the fourth view. }) return t.render(c)
A propsito no hemos usado el atajo render_to_response en estos ejemplos manualmente cargamos las plantillas, construimos el contexto y renderizamos las plantillas. Simplemente por claridad, estamos demostrando todos los pasos necesarios. Cada vista pasa las mismas tres variables app, user y ip_address a su plantilla. No sera bueno poder eliminar esa redundancia? RequestContext y los procesadores de contexto fueron creado para resolver este problema. Los procesadores de contexto te permiten especicar un nmero de variables que son incluidas automticamente en cada contexto sin la necesidad de tener que hacerlo manualmente en cada llamada a render_to_response(). El secreto est en utilizar RequestContext en lugar de Context cuando renderizamos una plantilla. La forma de nivel ms bajo de usar procesadores de contexto es crear algunos de ellos y pasarlos a RequestContext. A continuacin mostramos como el ejemplo anterior puede lograrse utilizando procesadores de contexto:
from django.template import loader, RequestContext def custom_proc(request): " A context processor that provides app , user return { app : My app , user : request.user, ip_address : request.META[ REMOTE_ADDR ] } def view_1(request): # ... t = loader.get_template( template1.html ) c = RequestContext(request, { message : I am view 1. }, processors=[custom_proc]) return t.render(c) def view_2(request): # ... t = loader.get_template( template2.html ) c = RequestContext(request, { message : I am the second view. }, processors=[custom_proc]) return t.render(c) def view_3(request): # ... t = loader.get_template( template3.html ) c = RequestContext(request, { message : I am the third view. }, processors=[custom_proc]) return t.render(c) def view_4(request): # ... t = loader.get_template( template4.html ) c = RequestContext(request, { message : I am the fourth view. }, processors=[custom_proc]) return t.render(c)
and
ip_address . "
123
Inspeccionemos paso a paso este cdigo: Primero, denimos una funcin custom_proc. Este es un procesador de contexto toma un objeto HttpRequest y devuelve un diccionario con variables a usar en el contexto de la plantilla. Eso es todo lo que hace. Hemos cambiado las cuatro vistas para que usen RequestContext en lugar de Context. Hay dos diferencias en cuanto a cmo el contexto es construido. Uno, RequestContext requiere que el primer argumento sea una instancia de HttpRequest la cual fue pasada a la vista en primer lugar (request). Dos, RequestContext recibe un parmetro opcional processors, el cual es una lista o tupla de funciones procesadoras de contexto a utilizar. En este caso, pasamos custom_proc, nuestro procesador de contexto denido previamente. Ya no es necesario en cada vista incluir app, user o ip_address cuando construimos el contexto, ya que ahora estas variables son provistas por custom_proc. Cada vista an posee la exibilidad como para introducir una o ms variables en el contexto de la plantilla si es necesario. En este ejemplo, la variable de plantilla message es creada de manera diferente en cada una de las vistas. En el Captulo 4, presentamos el atajo render_to_response(), el cual nos ahorra tener que llamar a loader.get_template(), luego crear un Context y ademas, llamar al mtodo render() en la plantilla. Para demostrar el funcionamiento a bajo nivel de los procesadores de contexto, en los ejemplos anteriores no hemos utilizado render_to_response(), pero es posible y preferible utilizar los procesadores de contexto junto a render_to_response(). Esto lo logramos mediante el argumento context_instance de la siguiente manera:
from django.shortcuts import render_to_response from django.template import RequestContext def custom_proc(request): " A context processor that provides app , user return { app : My app , user : request.user, ip_address : request.META[ REMOTE_ADDR ] } def view_1(request): # ... return render_to_response( template1.html , { message : I am view 1. }, context_instance=RequestContext(request, processors=[custom_proc])) def view_2(request): # ... return render_to_response( template2.html , { message : I am the second view. }, context_instance=RequestContext(request, processors=[custom_proc])) def view_3(request): # ... return render_to_response( template3.html , { message : I am the third view. }, context_instance=RequestContext(request, processors=[custom_proc])) def view_4(request): # ... return render_to_response( template4.html , { message : I am the fourth view. }, context_instance=RequestContext(request, processors=[custom_proc]))
and
ip_address . "
Aqu, hemos logrado reducir el cdigo para renderizar las plantillas en cada vista a una sola lnea. 124 Captulo 11. Captulo 10: Extender el sistema de plantillas
Esto es una mejora, pero, evaluando la concisin de este cdigo, debemos admitir que hemos logrado reducir la redundancia en los datos (nuestras variables de plantilla), pero aun as, estamos especicando una y otra vez nuestro contexto. Es decir, hasta ahora usar procesadores de contexto no nos ahorra mucho cdigo si tenemos que escribir processors constantemente. Por esta razn, Django admite el uso de procesadores de contexto globales. El parmetro de conguracin TEMPLATE_CONTEXT_PROCESSORS designa cuales sern los procesadores de contexto que debern ser aplicados siempre a RequestContext. Esto elimina la necesidad de especicar processors cada vez que utilizamos RequestContext. TEMPLATE_CONTEXT_PROCESSORS tiene, por omisin, el siguiente valor:
TEMPLATE_CONTEXT_PROCESSORS = ( django.core.context_processors.auth , django.core.context_processors.debug , django.core.context_processors.i18n , django.core.context_processors.media , )
Este parmetro de conguracin es una tupla de funciones que utilizan la misma interfaz que nuestra funcin custom_proc utilizada previamente funciones que toman un objeto HttpRequest como primer argumento, y devuelven un diccionario de items que sern incluidos en el contexto de la plantilla. Ten en cuenta que los valores en TEMPLATE_CONTEXT_PROCESSORS son especicados como strings, lo cual signica que estos procesadores debern estar en algn lugar dentro de tu PYTHONPATH (para poder referirse a ellos desde el archivo de conguracin) Estos procesadores de contexto son aplicados en orden, es decir, si uno de estos procesadores aade una variable al contexto y un segundo procesador aade otra variable con el mismo nombre, entonces la segunda sobre-escribir a la primera. Django provee un numero de procesadores de contexto simples, entre ellos los que estn activos por defecto.
11.2.1 django.core.context_processors.auth
Si TEMPLATE_CONTEXT_PROCESSORS contiene este procesador, cada RequestContext contendr las siguientes variables: user: Una instancia de django.contrib.auth.models.User representando al usuario actualmente autenticado (o una instancia de AnonymousUser si el cliente no se ha autenticado an). messages: Una lista de mensajes (como string) para el usuario actualmente autenticado. Detrs del teln, esta variable llama a request.user.get_and_delete_messages() para cada request. Este mtodo colecta los mensajes del usuario, y luego los borra de la base de datos. perms: Instancia de django.core.context_processors.PermWrapper, la cual representa los permisos que posee el usuario actualmente autenticado. En el Captulo 12 encontrars ms informacin acerca de usuarios, permisos y mensajes.
11.2.2 django.core.context_processors.debug
Este procesador aade informacin de depuracin a la capa de plantillas. Si TEMPLATE_CONTEXT_PROCESSORS contiene este procesador, cada RequestContext contendr las siguientes variables: debug: El valor del parmetro de conguracin DEBUG (True o False). Esta variable puede usarse en las plantillas para saber si ests en modo de depuracin o no.
125
sql_queries: Una lista de diccionarios {sql: ..., time: ...} representando todas las consultas SQL que se generaron durante la peticin (request) y cunto duraron. La lista est ordenada respecto a cundo fue ejecutada cada consulta. Como la informacin de depuracin es sensible, este procesador de contexto slo agregar las variables al contexto si las dos siguientes condiciones son verdaderas. El parmetro de conguracin DEBUG es True La solicitud (request) viene de una direccin IP listada en el parmetro de conguracin INTERNAL_IPS.
11.2.3 django.core.context_processors.i18n
Si este procesador est habilitado, cada RequestContext contendr las siguientes variables: LANGUAGES: El valor del parmetro de conguracin LANGUAGES. LANGUAGE_CODE: request.LANGUAGE_CODE si existe; de lo contrario, el valor del parmetro de conguracin LANGUAGE_CODE. En el Apndice E se especica ms informacin sobre estos parmetros.
11.2.4 django.core.context_processors.request
Si este procesador est habilitado, cada RequestContext contendr una variable request, la cual es el actual objeto HttpRequest. Este procesador no est habilitado por defecto.
126
django.template.loader.select_template(template_name_list): select_template es similar a get-template, excepto que recibe una lista de nombres de plantillas. Retorna la primera plantilla de dicha lista que existe. Si ninguna de las plantillas existe se lanzar una excepcin TemplateDoesNotExist. Como se vio en el Captulo 4, cada una de esas funciones usan por omisin el valor de tu variable de conguracin TEMPLATE_DIRS para cargar las plantillas. Sin embargo, internamente las mismas delegan la tarea pesada a un cargador de plantillas. Algunos de los cargadores estn, por omisin, desactivados pero puedes activarlos editando la variable de conguracin TEMPLATE_LOADERS. TEMPLATE_LOADERS debe ser una tupla de cadenas, donde cada cadena representa un cargador de plantillas. Estos son los cargadores de plantillas incluidos con Django: django.template.loaders.filesystem.load_template_source: Este cargador carga plantillas desde el sistema de archivos, de acuerdo a TEMPLATE_DIRS. Por omisin est activo. django.template.loaders.app_directories.load_template_source: Este cargador carga plantillas desde aplicaciones Django en el sistema de archivos. Para cada aplicacin en INSTALLED_APPS, el cargador busca un sub-directorio templates. Si el directorio existe, Django buscar una plantilla en el mismo. Esto signica que puedes almacenar plantillas en tus aplicaciones individuales, facilitando la distribucin de aplicaciones Django con plantillas por omisin. Por ejemplo si INSTALLED_APPS contiene (myproject.polls, myproject.music) entonces get_template(foo.html) buscar plantillas en el siguiente orden: /path/to/myproject/polls/templates/foo.html /path/to/myproject/music/templates/foo.html Notar que el cargador realiza una optimizacin cuando es importado por primera vez: hace caching de una lista de cuales de los paquetes en INSTALLED_APPS tienen un sub-directorio templates. Por omisin este cargador est activo. django.template.loaders.eggs.load_template_source: Este cargador es bsicamente idntico a app_directories, excepto que carga las plantillas desde eggs Python en lugar de hacerlo desde el sistema de archivos. Por omisin este cargador est desactivado; necesitars activarlo si ests usando eggs para distribuir tu aplicacin. Django usa los cargadores de plantillas en el orden en el que aparecen en la variable de conguracin TEMPLATE_DIRS. Usar cada uno de los cargadores hasta que uno de los mismos tenga xito en la bsqueda de la plantilla.
127
La creacin de una biblioteca para plantillas es un proceso de dos pasos: Primero, decidir qu aplicacin Django alojar la biblioteca. Si has creado una aplicacin va manage.py startapp puedes colocarla all, o puedes crear otra aplicacin con el solo n de alojar la biblioteca. Sin importar cual de las dos rutas tomes, asegrate de agregar la aplicacin a tu variable de conguracin INSTALLED_APPS. Explicaremos esto un poco ms adelante. Segundo, crear un directorio templatetags en el paquete de aplicacin Django apropiado. Debe encontrarse en el mismo nivel que models.py, views.py, etc. Por ejemplo:
books/ __init__.py models.py templatetags/ views.py
Crea dos archivos vacos en el directorio templatetags: un archivo __init__.py (para indicarle a Python que se trata de un paquete que contiene cdigo Python) y un archivo que contendr tus deniciones personalizadas de etiquetas/ltros. El nombre del segundo archivo es el que usars para cargar las etiquetas ms tarde. Por ejemplo, si tus etiquetas/ltros personalizadas estn en un archivo llamado poll_extras.py, entonces debers escribir lo siguiente en una plantilla:
{ % load poll_extras %}
La etiqueta { % load %} examina tu variable de conguracin INSTALLED_APPS y slo permite la carga de bibliotecas para plantillas desde aplicaciones Django que estn instaladas. Se trata de una caracterstica de seguridad; te permite tener en cierto equipo el cdigo Python de varias bibliotecas para plantillas sin tener que activar el acceso a todas ellas para cada instalacin de Django. Si escribes una biblioteca para plantillas que no se encuentra atada a ningn modelo/vista particular es vlido y normal el tener un paquete de aplicacin Django que slo contiene un paquete templatetags. No existen lmites en lo referente a cuntos mdulos puedes poner en el paquete templatetags. Slo ten presente que una sentencia { % load %} cargar etiquetas/ltros para el nombre del mdulo Python provisto, no el nombre de la aplicacin. Una vez que has creado ese mdulo Python, slo tendrs que escribir un poquito de cdigo Python, dependiendo de si ests escribiendo ltros o etiquetas. Para ser una biblioteca de etiquetas vlida, el mdulo debe contener una variable a nivel del mdulo llamada register que sea una instancia de template.Library. Esta instancia de template.Library es la estructura de datos en la cual son registradas todas las etiquetas y ltros. As que inserta en la zona superior de tu mdulo, lo siguiente:
from django import template register = template.Library()
Nota: Para ver un buen nmero de ejemplos, examina el cdigo fuente de los ltros y etiquetas incluidos con Django. Puedes encontrarlos en django/template/defaultfilters.py y django/template/defaulttags.py, respectivamente. Algunas aplicaciones en django.contrib tambin contienen bibliotecas para plantillas. Una vez que hayas creado esta variable register, usars la misma para crear ltros y etiquetas para plantillas.
128
La mayora de los ltros no reciben argumentos. En ese caso, basta con que no incluyas el argumento en tu funcin:
def lower(value): # Only one argument. " Converts a string into all lowercase " return value.lower()
Una vez que has escrito tu denicin de ltro, necesitas registrarlo en tu instancia de Library, para que est disponible para el lenguaje de plantillas de Django:
register.filter( cut , cut) register.filter( lower , lower)
El mtodo Library.filter() tiene dos argumentos: El nombre del ltro (una cadena) La funcin ltro propiamente dicha Si ests usando Python 2.4 o ms reciente, puedes usar register.filter() como un decorador:
@register.filter(name= cut ) def cut(value, arg): return value.replace(arg, ) @register.filter def lower(value): return value.lower()
Si no provees el argumento name, como en el segundo ejemplo, Django usar el nombre de la funcin como nombre del ltro. Veamos entonces el ejemplo completo de una biblioteca para plantillas, que provee el ltro cut:
from django import template register = template.Library() @register.filter(name= cut ) def cut(value, arg): return value.replace(arg, )
129
Nota: Si, esta etiqueta de plantilla es redundante La etiqueta { % now %} incluida en Django por defecto hace exactamente lo mismo con una sintaxis ms simple. Slo mostramos esta etiqueta a modo de ejemplo. Para evaluar esta funcin, se deber obtener el parmetro y crear el objeto Node:
from django import template def do_current_time(parser, token): try: # split_contents() knows not to split quoted strings. tag_name, format_string = token.split_contents() except ValueError: msg = %r tag requires a single argument % token.split_contents()[0] raise template.TemplateSyntaxError(msg) return CurrentTimeNode(format_string[1:-1])
Hay muchas cosas en juego aqu: parser es la instancia del parser. No lo necesitamos en este ejemplo. token.contents es un string con los contenidos crudos de la etiqueta, en nuestro ejemplo sera: current_time " %Y- %m- %d %I: %M %p. El mtodo token.split_contents() separa los argumentos en sus espacios, mientras deja unidas a los strings. Evite utilizar token.contents.split() (el cual usa la semntica natural de Python para
130
dividir strings, y por esto no es tan robusto, ya que divide en todos los espacios, incluyendo aquellos dentro de cadenas entre comillas. Esta funcin es la responsable de generar la excepcin django.template.TemplateSyntaxError con mensajes tiles, ante cualquier caso de error de sintaxis. No escribas el nombre de la etiqueta en el mensaje de error, ya que eso acoplara innecesariamente el nombre de la etiqueta a la funcin. En cambio, token.split_contents()[0] siempre contendr el nombre de tu etiqueta an cuando la etiqueta no lleve argumentos. La funcin devuelve CurrentTimeNode (el cual mostraremos en un momento) conteniendo todo lo que el nodo necesita saber sobre esta etiqueta. En este caso, slo pasa el argumento " %Y- %m- %d %I: %M %p". Las comillas son removidas con format_string[1:-1]. Las funciones de compilacin de etiquetas de plantilla deben devolver una subclase de Nodo; cualquier otro valor es un error. Escribir el nodo de plantilla El segundo paso para escribir etiquetas propias, es denir una subclase de Node que posea un mtodo render(). Continuando con el ejemplo previo, debemos denir CurrentTimeNode:
import datetime class CurrentTimeNode(template.Node): def __init__(self, format_string): self.format_string = format_string def render(self, context): now = datetime.datetime.now() return now.strftime(self.format_string)
Estas dos funciones (__init__ y render) se relacionan directamente con los dos pasos para el proceso de la plantilla (compilacin y renderizado). La funcin de inicializacin slo necesitar almacenar el string con el formato deseado, el trabajo real sucede dentro de la funcin render() Del mismo modo que los ltros de plantilla, estas funciones de renderizacin deberan fallar silenciosamente en lugar de generar errores. En el nico momento en el cual se le es permitido a las etiquetas de plantilla generar errores es en tiempo de compilacin. Registrar la etiqueta Finalmente, debers registrar la etiqueta con tu objeto Library dentro del mdulo. Registrar nuevas etiquetas es muy similar a registrar nuevos ltros (como explicamos previamente). Slo debers instanciar un objeto template.Library y llamar a su mtodo tag(). Por ejemplo:
register.tag( current_time , do_current_time)
El mtodo tag() toma dos argumentos: El nombre de la etiqueta de plantilla (string). Si esto se omite, se utilizar el nombre de la funcin de compilacin. La funcin de compilacin. De manera similar a como sucede con el registro de ltros, tambin es posible utilizar register.tag como un decorador en Python 2.4 o posterior:
@register.tag(name="current_time") def do_current_time(parser, token): # ...
131
Si omitimos el argumento name, as como en el segundo ejemplo, Django usar el nombre de la funcin como nombre de la etiqueta. Denir una variable en el contexto El ejemplo en la seccin anterior simplemente devuelve un valor. Muchas veces es til denir variables de plantilla en vez de simplemente devolver valores. De esta manera, los autores de plantillas podrn directamente utilizar las variables que esta etiqueta dena. Para denir una variable en el contexto, asignaremos a nuestro objeto context disponible en el mtodo render() nuestras variables, como si de un diccionario se tratase. Aqu mostramos la versin actualizada de CurrentTimeNode que dene una variable de plantilla, current_time, en lugar de devolverla:
class CurrentTimeNode2(template.Node): def __init__(self, format_string): self.format_string = format_string def render(self, context): now = datetime.datetime.now() context[ current_time ] = now.strftime(self.format_string) return
Devolvemos un string vaco, debido a que render() siempre debe devolver un string. Entonces, si todo lo que la etiqueta hace es denir una variable, render() debe al menos devolver un string vaco. De esta manera usaramos esta nueva versin de nuestra etiqueta:
{ % current_time2 " %Y- %M- %d %I: %M %p" %} <p>The time is {{ current_time }}.</p>
Pero hay un problema con CurrentTimeNode2: el nombre de la variable current_time est denido dentro del cdigo. Esto signica que tendrs que asegurar que {{ current_time }} no sea utilizado en otro lugar dentro de la plantilla, ya que { % current_time %} sobreescribir el valor de esa otra variable. Una solucin ms limpia, es poder recibir el nombre de la variable en la etiqueta de plantilla as:
{ % get_current_time " %Y- %M- %d %I: %M %p" as my_current_time %} <p>The current time is {{ my_current_time }}.</p>
Para hacer esto, necesitaremos modicar tanto la funcin de compilacin como la clase Node de esta manera:
import re class CurrentTimeNode3(template.Node): def __init__(self, format_string, var_name): self.format_string = format_string self.var_name = var_name def render(self, context): now = datetime.datetime.now() context[self.var_name] = now.strftime(self.format_string) return def do_current_time(parser, token):
132
# This version uses a regular expression to parse tag contents. try: # Splitting by None == splitting by spaces. tag_name, arg = token.contents.split(None, 1) except ValueError: msg = %r tag requires arguments % token.contents[0] raise template.TemplateSyntaxError(msg) m = re.search( r (.*?) as ( \ w+) , arg) if m: fmt, var_name = m.groups() else: msg = %r tag had invalid arguments % tag_name raise template.TemplateSyntaxError(msg) if not (fmt[0] == fmt[-1] and fmt[0] in ( " , " " )): msg = " %r tag s argument should be in quotes " % tag_name raise template.TemplateSyntaxError(msg) return CurrentTimeNode3(fmt[1:-1], var_name)
Ahora, do_current_time() pasa el string de formato junto al nombre de la variable a CurrentTimeNode3. Evaluar hasta otra etiqueta de bloque Las etiquetas de plantilla pueden funcionar como bloques que contienen otras etiquetas (piensa en { % if %}, { % for %}, etc.). Para crear una etiqueta como esta, usa parser.parse() en tu funcin de compilacin. Aqu vemos como est implementada la etiqueta estndar { % coment %}:
def do_comment(parser, token): nodelist = parser.parse(( endcomment ,)) parser.delete_first_token() return CommentNode() class CommentNode(template.Node): def render(self, context): return
parser.parse() toma una tupla de nombres de etiquetas de bloque para evaluar y devuelve una instancia de django.template.NodeList, la cual es una lista de todos los objetos Nodo que el parser encontr antes de haber encontrado alguna de las etiquetas nombradas en la tupla. Entonces, en el ejemplo previo, nodelist es una lista con todos los nodos entre { % comment %} y { % endcomment %}, excluyendo a los mismos { % comment %} y { % endcomment %}. Luego de que parser.parse() es llamado el parser an no ha consumido la etiqueta { % endcomment %}, es por eso que en el cdigo se necesita llamar explcitamente a parser.delete_first_token() para prevenir que esta etiqueta sea procesada nuevamente. Luego, CommentNode.render() simplemente devuelve un string vaco. Cualquier cosa entre { % comment %} y { % endcomment %} es ignorada.
Evaluar hasta otra etiqueta de bloque y guardar el contenido
En el ejemplo anterior, do_comment() desech todo entre { % comment %} y { % endcomment %}, pero tambin es posible hacer algo con el cdigo entre estas etiquetas.
133
Por ejemplo, presentamos una etiqueta de plantilla, { % upper %}, que convertir a maysculas todo hasta la etiqueta { % endupper %}:
{ % upper %} This will appear in uppercase, {{ your_name }}. { % endupper %}
Como en el ejemplo previo, utilizaremos parser.parse() pero esta vez pasamos el resultado en nodelist a Node:
@register.tag def do_upper(parser, token): nodelist = parser.parse(( endupper ,)) parser.delete_first_token() return UpperNode(nodelist) class UpperNode(template.Node): def __init__(self, nodelist): self.nodelist = nodelist def render(self, context): output = self.nodelist.render(context) return output.upper()
El nico concepto nuevo aqu es self.nodelist.render(context) en UpperNode.render(). El mismo simplemente llama a render() en cada Node en la lista de nodos. Para ms ejemplos de renderizado complejo, examina el cdigo fuente para las etiquetas { % if %}, { % for %}, { % ifequal %} y { % ifchanged %}. Puedes encontrarlas en django/template/defaulttags.py.
Un par de cosas a tener en cuenta acerca de la funcin auxiliar simple_tag: Slo se pasa un argumento a nuestra funcin. La vericacin de la cantidad requerida de argumentos ya ha sido realizada para el momento en el que nuestra funcin es llamada, de manera que no es necesario que lo hagamos nosotros.
134
Las comillas alrededor del argumento (si existieran) ya han sido quitadas, de manera que recibimos una cadena comn.
Primero denimos la funcin que toma el argumento y produce un diccionario de datos con los resultados. Nota que nos basta un diccionario y no necesitamos retornar nada ms complejo. Esto ser usado como el contexto para el fragmento de plantilla:
def show_books_for_author(author): books = author.book_set.all() return { books : books}
Luego creamos la plantilla usada para renderizar la salida de la etiqueta. Siguiendo con nuestro ejemplo, la plantilla es muy simple:
<ul> { % for book in books %} <li> {{ book }} </li> { % endfor %} </ul>
Finalmente creamos y registramos la etiqueta de inclusin invocando el mtodo inclusion_tag() sobre un objeto Library. Continuando con nuestro ejemplo, si la plantilla se encuentra en un archivo llamado polls/result_snippet.html, registraremos la plantilla de la siguiente manera:
register.inclusion_tag( books/books_for_author.html )(show_books_for_author)
Como siempre, la sintaxis de decoradores de Python 2.4 tambin funciona, de manera que en cambio podramos haber escrito:
@register.inclusion_tag( books/books_for_author.html ) def show_books_for_author(show_books_for_author): ...
A veces tus etiquetas de inclusin necesitan tener acceso a valores del contexto de la plantilla padre. Para resolver esto Django provee una opcin takes_context para las etiquetas de inclusin. Si especicas takes_context cuando creas una etiqueta de plantilla, la misma no tendr argumentos obligatorios
135
y la funcin Python subyacente tendr un argumento: el contexto de la plantilla en el estado en el que se encontraba cuando la etiqueta fue invocada. Por ejemplo supongamos que ests escribiendo una etiqueta de inclusin que ser siempre usada en un contexto que contiene variables home_link y home_title que apuntan a la pgina principal. As es como se vera la funcin Python:
@register.inclusion_tag( link.html , takes_context=True) def jump_link(context): return { link : context[ home_link ], title : context[ home_title ], }
Nota: El primer parmetro de la funcin debe llamarse context. La plantilla link.html podra contener lo siguiente:
Jump directly to <a href="{{ link }}">{{ title }}</a>.
Entonces, cada vez que desees usar esa etiqueta personalizada, carga su biblioteca y ejectala sin argumentos, de la siguiente manera:
{ % jump_link %}
El argumento template_name es el nombre de la plantilla a cargar (tal como fue pasado a loader.get_template() o loader.select_template()) y template_dirs es una lista opcional de directorios en los que se buscar en lugar de TEMPLATE_DIRS. Si un cargador es capaz de cargar en forma exitosa una plantilla, debe retornar una tupla: (template_source, template_path). Donde template_source es la cadena de plantilla que ser compilada por la maquinaria de plantillas, y template_path es la ruta desde la cual fue cargada la plantilla. Dicha ruta podra ser presentada al usuario para nes de depuracin as que debe identicar en forma rpida desde dnde fue cargada la plantilla. Si al cargador no le es posible django.template.TemplateDoesNotExist. cargar una plantilla, debe lanzar
Cada funcin del cargador debe tambin poseer un atributo de funcin is_usable. Este es un Booleano que le informa a la maquinaria de plantillas si este cargador est disponible en la instalacin de Python actual. Por ejemplo el cargador desde eggs (que es capaz de cargar plantillas desde eggs Python) ja is_usable a False si el mdulo pkg_resources no se encuentra instalado, porque pkg_resources es necesario para leer datos desde eggs. Un ejemplo ayudar a claricar todo esto. Aqu tenemos una funcin cargadora de plantillas que puede cargar plantillas desde un archivo ZIP. Usa una variable de conguracin personalizada 136 Captulo 11. Captulo 10: Extender el sistema de plantillas
TEMPLATE_ZIP_FILES como una ruta de bsqueda en lugar de TEMPLATE_DIRS y espera que cada tem en dicha ruta sea un archivo ZIP contiendo plantillas:
import zipfile from django.conf import settings from django.template import TemplateDoesNotExist def load_template_source(template_name, template_dirs=None): """Template loader that loads templates from a ZIP file.""" template_zipfiles = getattr(settings, " TEMPLATE_ZIP_FILES " , []) # Try each ZIP file in TEMPLATE_ZIP_FILES. for fname in template_zipfiles: try: z = zipfile.ZipFile(fname) source = z.read(template_name) except (IOError, KeyError): continue z.close() # We found a template, so return the source. template_path = " %s : %s " % (fname, template_name) return (source, template_path) # If we reach here, the template couldnt be loaded raise TemplateDoesNotExist(template_name) # This loader is always usable (since zipfile is included with Python) load_template_source.is_usable = True
El nico paso restante si deseamos usar este cargador es agregarlo a la variable de conguracin TEMPLATE_LOADERS. Si pusiramos este cdigo en un paquete llamado mysite.zip_loader entonces agregaremos mysite.zip_loader.load_template_source a TEMPLATE_LOADERS.
137
En forma conjunta, las pginas de documentacin deberan proveerte cada etiqueta, ltro, variable y objeto disponible para su uso en una plantilla arbitraria.
11.8 Qu sigue?
Hasta ahora este libro ha asumido que el contenido que ests visualizando es HTML. Esta no es una suposicin incorrecta para un libro sobre desarrollo Web, pero en algunas ocasiones querrs usar Django para generar otros formatos de datos. El prximo captulo describe cmo puedes usar Django para producir imgenes, PDFs y cualquier otro formato de datos que puedas imaginar.
138
CAPTULO 12
Usualmente cuando hablamos sobre desarrollo de sitios Web, hablamos de producir HTML. Por supuesto, hay mucho ms que contenido HTML en la Web; la usamos para distribuir datos en todo tipo de formatos: RSS, PDFs, imgenes, y as sucesivamente. Hasta ahora nos hemos concentrado en el caso comn de la produccin de HTML, pero en ese captulo tomaremos un desvo y veremos cmo usar Django para producir otro tipo de contenido. Django posee varias herramientas tiles que puedes usar para producir algunos tipos comunes de contenido no HTML: Feeds de sindicacin RSS/Atom Mapas de sitios haciendo uso de Sitemaps (un formato XML originalmente desarrollado por Google que provee de ayuda a motores de bsqueda) Examinaremos cada una de esas herramientas un poco ms adelante, pero antes cubriremos los principios bsicos.
139
image_data = open( " /path/to/my/image.png " , " rb " ).read() return HttpResponse(image_data, mimetype= " image/png " )
Eso es todo! Si sustituimos la ruta de la imagen en la llamada a open() con la ruta a una imagen real, podemos usar esta vista bastante sencilla para servir una imagen, y el navegador la mostrar correctamente. La otra cosa importante a tener presente es que los objetos HttpResponse implementan el API estndar de Python para cheros. Esto signica que podemos usar una instancia de HttpResponse en cualquier lugar donde Python (o biblioteca de terceros) espera un chero. Como un ejemplo de cmo funciona esto, veamos la produccin de CSV con Django.
Nota: El listado precedente contiene nmeros reales; cortesa de la Administracin Federal de Aviacin (FAA) de E.E.U.U. Vea http://www.faa.gov/data_statistics/passengers_cargo/unruly_passengers/. Aunque CSV parezca simple, no es un formato que ha sido denido formalmente. Diferentes programas producen y consumen diferentes variantes de CSV, haciendo un poco complicado usarlo. Afortunadamente, Python incluye una biblioteca estndar para CSV, csv, que es bastante robusta. Debido a que el mdulo csv opera sobre objetos similares a cheros, es muy fcil usar un HttpResponse en lugar de un chero:
import csv from django.http import HttpResponse # Nmero de pasajeros problematicos por ao entre 1995 - 2005. En una aplicacin real # esto vendra desde una base de datos o cualquier otro medio de almacenamiento. UNRULY_PASSENGERS = [146,184,235,200,226,251,299,273,281,304,203] def unruly_passengers_csv(request): # Creamos el objeto Httpresponse con la cabecera CSV apropiada. response = HttpResponse(mimetype= text/csv ) response[ Content-Disposition ] = attachment; filename=unruly.csv # Creamos un escritor CSV usando a HttpResponse como "fichero" writer = csv.writer(response)
140
writer.writerow([ Year , Unruly Airline Passengers ]) for (year, num) in zip(range(1995, 2006), UNRULY_PASSENGERS): writer.writerow([year, num]) return response
El cdigo y los comentarios deberan ser bastante claros, pero hay unas pocas cosas que merecen mencin especial: Se le da a la respuesta el tipo MIME text/csv (en lugar del tipo predeterminado text/html). Esto le dice a los navegadores que el documento es un chero CSV. La respuesta obtiene una cabecera Content-Disposition adicional, la cual contiene el nombre del chero CSV. Esta cabecera (bueno, la parte adjunta) le indicar al navegador que solicite la ubicacin donde guardar el chero (en lugar de simplemente mostrarlo). El nombre de chero es arbitrario; llmalo como quieras. Ser usado por los navegadores en el cuadro de dilogo Guardar como... Usar el API de generacin de CSV es sencillo: basta pasar response como primer argumento a csv.writer. La funcin csv.writer espera un objeto de tipo chero, y los de tipo HttpResponse se ajustan. Por cada la en el chero CSV, invocamos a writer.writerow, pasndole un objeto iterable como una lista o una tupla. El mdulo CSV se encarga de poner comillas por ti, as que no tendrs que preocuparte por escapar caracteres en las cadenas que tengan comillas o comas en su interior. Limtate a pasar la informacin a writerow(), que har lo correcto. Este es el patrn general que usars siempre que necesites retornar contenido no HTML: crear un objeto HttpResponse de respuesta (con un tipo MIME especial), pasrselo a algo que espera un chero, y luego devolver la respuesta. Veamos unos cuntos ejemplos ms.
141
Si ests usando una distribucin moderna de Linux, podras desear comprobar con la utilidad de manejo de paquetes de software antes de instalar ReportLab. La mayora de los repositorios de paquetes ya incluyen ReportLab. Por ejemplo, si ests usando la (excelente) distribucin Ubuntu, un simple apt-get install python-reportlab har la magia necesaria. Prueba tu instalacin importando la misma en el intrprete interactivo Python:
>>> import reportlab
Son necesarias alguna notas: Usamos el tipo MIME application/pdf. Esto le indica al navegador que el documento es un chero PDF y no un chero HTML. Si no incluyes esta informacin, los navegadores web probablemente interpretarn la respuesta como HTML, lo que resultar en jeroglcos en la ventana del navegador. Interactuar con la API ReportLab es sencillo: slo pasa response como el primer argumento a canvas.Canvas. La clase Canvas espera un objeto le-like, y los objetos HttpResponse se ajustarn a la norma. Todos los mtodos de generacin de PDF subsecuentes son llamados pasndoles el objeto PDF (en este caso p), no response. Finalmente, es importante llamar a los mtodos showPage() y save() del objeto PDF (de otra manera obtendrs un chero PDF corrupto).
142
La biblioteca cStringIO provee una interfaz va objetos le-like que est escrita en C para mxima eciencia. Ese es el ejemplo Hola Mundo anterior modicado para usar cStringIO:
from cStringIO import StringIO from reportlab.pdfgen import canvas from django.http import HttpResponse def hello_pdf(request): # Create the HttpResponse object with the appropriate PDF headers. response = HttpResponse(mimetype= application/pdf ) response[ Content-Disposition ] = attachment; filename=hello.pdf temp = StringIO() # Create the PDF object, using the StringIO object as its "file." p = canvas.Canvas(temp) # Draw things on the PDF. Heres where the PDF generation happens. # See the ReportLab documentation for the full list of functionality. p.drawString(100, 100, " Hello world. " ) # Close the PDF object cleanly. p.showPage() p.save() # Get the value of the StringIO buffer and write it to the response. response.write(temp.getvalue()) return response
143
Ahora que hemos visto lo bsico de generar contenido no-HTML, avancemos al siguiente nivel de abstraccin. Django incluye algunas herramientas bonitas e ingeniosas para generar cierto tipo de contenido no-HTML.
12.5.1 Inicializacin
Para activar los feeds de sindicacin en tu sitio Django, agrega lo siguiente en tu URLconf:
( r ^feeds/(?P<url>.*)/$ , django.contrib.syndication.views.feed , { feed_dict : feeds} ),
Esa lnea le indica a Django que use el framework RSS para captar las URLs que comienzan con "feeds/". (Puedes cambiar "feeds/" por algo que se adapte a tus necesidades). Esta lnea de URLconf tiene un argumento extra: {feed_dict: feeds}. Usa este argumento extra para pasar al framework de feeds de sindicacin los feeds que deben ser publicados en dicha URL. Especcamente, feed_dict debe ser un diccionario que mapee el slug (etiqueta corta de URL) de un feed a la clase Feed. Puedes denir el feed_dict en el mismo URLconf. Este es un ejemplo completo de URLconf:
from django.conf.urls.defaults import * from myproject.feeds import LatestEntries, LatestEntriesByCategory feeds = { latest : LatestEntries, categories : LatestEntriesByCategory, } urlpatterns = patterns( , # ... ( r ^feeds/(?P<url>.*)/$ , django.contrib.syndication.views.feed , { feed_dict : feeds}), # ... )
El ejemplo anterior registra dos feeds: 144 Captulo 12. Captulo 11: Generacin de contenido no HTML
El feed representado por LatestEntries residir en feeds/latest/. El feed representado por LatestEntriesByCategory residir en feeds/categories/. Una vez que este congurado, necesitas denir la propia clase Feed. Una clase Feed es una simple clase Python que representa un feed de sindicacin. Un feed puede ser simple (p. ej. noticias del sitio, o una lista de las ltimas entradas del blog) o ms complejo (p. ej. mostrar todas las entradas de un blog en una categora en particular, donde la categora es variable). La clase Feed debe ser una subclase de django.contrib.syndication.feeds.Feed. Esta puede residir en cualquier parte del rbol de cdigo.
Las cosas importantes a tener en cuenta son: La clase es subclase de django.contrib.syndication.feeds.Feed. title, link, y description corresponden a los elementos RSS estndar <title>, <link>, y <description> respectivamente. items() es simplemente un mtodo que retorna una lista de objetos que deben incluirse en el feed como elementos <item>. Aunque este ejemplo retorna objetos NewsItem usando la API de base de datos de Django, no es un requerimiento que items() deba retornar instancias de modelos. Obtienes unos pocos bits de funcionalidad gratis usando los modelos de Django, pero items() puede retornar cualquier tipo de objeto que desees. Hay solamente un paso ms. En un feed RSS, cada <item> posee <title>, <link>, y <description>. Necesitamos decirle al framework qu datos debe poner en cada uno de los elementos. Para especicar el contenido de <title> y <description>, crea plantillas Django (ver Captulo 4) llamadas feeds/latest_title.html y feeds/latest_description.html, donde latest es el slug especicado en URLconf para el feed dado. Notar que la extensin .html es requerida. El sistema RSS renderiza dicha plantilla por cada tem, pasndole dos variables de contexto para plantillas: obj: El objeto actual (uno de los tantos que retorna en items()). site: Un objeto django.models.core.sites.Site representa el sitio actual. Esto es til para {{ site.domain }} o {{ site.name }}. Si no creas una plantilla para el ttulo o la descripcin, el framework utilizar la plantilla por omisin "{{ obj }}" exacto, la cadena normal de representacin del objeto.
145
Tambin puedes cambiar los nombres de estas plantillas especicando title_template y description_template como atributos de tu clase Feed. Para especicar el contenido de <link>, hay dos opciones. Por cada tem en items(), Django primero tratar de ejecutar el mtodo get_absolute_url() en dicho objeto. Si dicho mtodo no existe, entonces trata de llamar al mtodo item_link() en la clase Feed, pasndole un nico parmetro, item, que es el objeto en s mismo. Ambos get_absolute_url() y item_link() deben retornar la URL del tem como una cadena normal de Python. Para el ejemplo anterior LatestEntries, podemos usar plantillas de feed muy simples. latest_title.html contiene:
{{ obj.title }}
y latest_description.html contiene:
{{ obj.description }}
%s " % obj.beat
146
%s " % obj.beat
Aqu tenemos el algoritmo bsico del framework RSS, asumiendo esa clase y un requerimiento a la URL /rss/beats/0613/: 1. El framework toma la URL /rss/beats/0613/ y nota que la URL contiene una parte extra tras el slug. Separa esa cadena remanente por el carcter /" y llama al mtodo get_object() de la clase Feed pasndole los trozos (bits) resultantes. En este caso, los trozos son [0613]. Para un /rss/beats/0613/foo/bar/, sern [0613, foo, bar]. requerimiento a
2. get_object() es el responsable de obtener el departamento requerido, a partir del bits dado. En este caso, usa la API de base de datos de Django para obtener el departamento. Notar que get_object() debe capturar la excepcin django.core.exceptions.ObjectDoesNotExist si recibe parmetros invlidos. No hay try/except abarcando la llamada a Beat.objects.get() porque no es necesario. Esa funcin, ante una falla lanza la excepcin Beat.DoesNotExist, y Beat.DoesNotExist es una subclase de ObjectDoesNotExist. Lanzar la excepcin ObjectDoesNotExist en get_object() le dice a Django que produzca un error 404 para el requerimiento en curso. 3. Para generar los campos <title>, <link>, y <description> del feed, Django usa los mtodos title(), link(), y description(). En el ejemplo anterior, se utilizaron atributos simples de clase string, pero este ejemplo muestra que estos pueden ser strings o mtodos. Por cada title, link, y description, Django sigue este algoritmo: 4. Trata de llamar al mtodo, pasando el argumento obj, donde obj es el objeto retornado por get_object(). 5. Si eso falla, trata de llamar al mtodo sin argumentos. 6. Si eso falla, usa los atributos de clase. 7. Finalmente, nota que items() en el ejemplo tambin toma como argumento a obj. El algoritmo para items es el mismo que se describe en el paso anterior primero prueba items(obj), despus items(), y nalmente un atributo de clase items (que debe ser una lista). La documentacin completa de todos los mtodos y clases Feed siempre esta disponible en la documentacin (http://www.djangoproject.com/documentation/0.96/syndication_feeds/). atributos de las ocial de Django
Nota que asignas como valor de feed_type una clase, no una instancia. Los tipos de feeds disponibles actualmente se muestran en la Tabla 11-1. Clase Feed django.utils.feedgenerator.Rss201rev2Feed django.utils.feedgenerator.RssUserland091Feed django.utils.feedgenerator.Atom1Feed Formato RSS 2.01 (por defecto) RSS 0.91 Atom 1.0
147
12.5.5 Enclosures
Para especicar enclosures (p. ej. recursos multimedia asociados al tem del feed tales como feeds de podcasts MP3), usa los ganchos item_enclosure_url, item_enclosure_length, y item_enclosure_mime_type, por ejemplo:
from myproject.models import Song class MyFeedWithEnclosures(Feed): title = " Example feed with enclosures " link = " /feeds/example-with-enclosures/ " def items(self): return Song.objects.all()[:30] def item_enclosure_url(self, item): return item.song_url def item_enclosure_length(self, item): return item.song_length item_enclosure_mime_type = " audio/mpeg "
Esto asume, por supuesto, que has creado un objeto Song con los campos song_url y song_length (p. ej. el tamao en bytes).
12.5.6 Idioma
Los Feeds creados por el framework de sindicacin incluyen automticamente la etiqueta <language> (RSS 2.0) o el atributo xml:lang apropiados (Atom). Esto viene directamente de tu variable de conguracin LANGUAGE_CODE.
12.5.7 URLs
El mtodo/atributo link puede retornar tanto una URL absoluta (p. ej. /blog/") como una URL con el nombre completo de dominio y protocolo (p. ej. "http://www.example.com/blog/"). Si link no retorna el dominio, el framework de sindicacin insertar el dominio del sitio actual, acorde a la variable de conguracin SITE_ID. Los feeds Atom requieren un <link rel="self"> que dene la ubicacin actual del feed. El framework de sindicacin completa esto automticamente, usando el dominio del sitio actual acorde a la variable de conguracin SITE_ID.
148
def items(self): return NewsItem.objects.order_by( -pub_date )[:5] class AtomSiteNewsFeed(RssSiteNewsFeed): feed_type = Atom1Feed
<?xml version="1.0" encoding="UTF-8"?> <urlset xmlns= "http://www.sitemaps.org/schemas/sitemap/0.9" > <url> <loc>http://www.djangoproject.com/documentation/</loc> <changefreq>weekly</changefreq> <priority>0.5</priority> </url> <url> <loc>http://www.djangoproject.com/documentation/0_90/</loc> <changefreq>never</changefreq> <priority>0.1</priority> </url> ... </urlset>
Para ms informacin sobre sitemaps, vea http://www.sitemaps.org/. El framework sitemap de Django automatiza la creacin de este chero XML si lo indicas expresamente en el cdigo Python. Para crear un sitemap, debes simplemente escribir una clase Sitemap y hacer referencia a la misma en tu URLconf.
12.6.1 Instalacin
Para instalar la aplicacin sitemap, sigue los siguientes pasos: 1. Agrega django.contrib.sitemaps a tu variable de conguracin INSTALLED_APPS.
149
2. Asegrate de que django.template.loaders.app_directories.load_template_source est en tu variable de conguracin TEMPLATE_LOADERS. Por omisin se encuentra activado, por lo que los cambios son necesarios solamente si modicaste dicha variable de conguracin. 3. Asegrate de que tienes instalado el framework sites (ver Captulo 14). Nota: La aplicacin sitemap no instala tablas en la base de datos. La nica razn de que est en INSTALLED_APPS es que el cargador de plantillas load_template_source pueda encontrar las plantillas includas.
12.6.2 Inicializacin
Para activar la generacin del sitemap en tu sitio Django, agrega la siguiente lnea a tu URLconf:
( r ^sitemap.xml$ , django.contrib.sitemaps.views.sitemap , { sitemaps : sitemaps})
Esta lnea le dice a Django que construya un sitemap cuando un cliente accede a /sitemap.xml. El nombre del chero sitemap no es importante, pero la ubicacin s lo es. Los motores de bsqueda solamente indexan los enlaces en tu sitemap para el nivel de URL actual y anterior. Por ejemplo, si sitemap.xml reside en tu directorio principal, el mismo puede hacer referencia a cualquier URL en tu sitio. Pero si tu sitemap reside en /content/sitemap.xml, solamente podr hacer referencia a URLs que comiencen con /content/. La vista sitemap toma un argumento extra: {sitemaps: sitemaps}. sitemaps debe ser un diccionario que mapee una etiqueta corta de seccin (p. ej. blog o news) a tu clase Sitemap (p.e., BlogSitemap o NewsSitemap). Tambin mapea hacia una instancia de una clase Sitemap (p. ej. BlogSitemap(some_var)).
150
Declarar un Sitemap debera verse muy similar a declarar un Feed; esto es justamente un objetivo del diseo. En manera similar a las clases Feed, los miembros de Sitemap pueden ser mtodos o atributos. Ver los pasos en la seccin Un feed ms complejo para ms informacin sobre cmo funciona esto. Una clase Sitemap puede denir los siguientes mtodos/atributos: items (requerido): Provee una lista de objetos. Al framework no le importa que tipo de objeto es; todo lo que importa es que los objetos sean pasados a los mtodos location(), lastmod(), changefreq(), y priority(). location (opcional): Provee la URL absoluta para el objeto dado. Aqu URL absoluta signica una URL que no incluye el protocolo o el dominio. Estos son algunos ejemplos: Bien: /foo/bar/ Mal: example.com/foo/bar/ Mal: http://example.com/foo/bar/ Si location no es provisto, el framework llamar al mtodo get_absolute_url() en cada uno de los objetos retornados por items(). lastmod (opcional): La fecha de ltima modicacin del objeto, como un objeto datetime de Python. changefreq (opcional): Cun a menudo el objeto cambia. Los valores posibles (segn indican las especicaciones de Sitemaps) son: always hourly daily weekly monthly yearly never priority (opcional): Prioridad sugerida de indexado entre 0.0 y 1.0. La prioridad por omisin de una pgina es 0.5; ver la documentacin de http://sitemaps.org para ms informacin de cmo funciona priority.
151
Sitemap Genrico La clase GenericSitemap trabaja con cualquier vista genrica (ver Captulo 9_) que pudieras poseer con anterioridad. Para usarla, crea una instancia, pasndola en el mismo info_dict que se pasa a la vista genrica. El nico requerimiento es que el diccionario tenga una entrada queryset. Tambin debe poseer una entrada date_field que especica un campo fecha para los objetos obtenidos del queryset. Esto ser usado por el atributo lastmod en el sitemap generado. Tambin puedes pasar los argumentos palabra clave (keyword) priority y changefreq al constructor GenericSitemap para especicar dichos atributos para todas las URLs. Este es un ejemplo de URLconf usando tanto, FlatPageSitemap como GenericSiteMap (con el anterior objeto hipottico Entry):
from django.conf.urls.defaults import * from django.contrib.sitemaps import FlatPageSitemap, GenericSitemap from mysite.blog.models import Entry info_dict = { queryset : Entry.objects.all(), date_field : pub_date , } sitemaps = { flatpages : FlatPageSitemap, blog : GenericSitemap(info_dict, priority=0.6), } urlpatterns = patterns( , # some generic view using info_dict # ... # the sitemap ( r ^sitemap.xml$ , django.contrib.sitemaps.views.sitemap , { sitemaps : sitemaps}) )
La vista django.contrib.sitemaps.views.sitemap debe tomar un parmetro que corresponde a una palabra clave, llamado section. As deberan verse las lneas relevantes en tu URLconf para el ejemplo anterior:
( r ^sitemap.xml$ , django.contrib.sitemaps.views.index , { sitemaps : sitemaps}), ( r ^sitemap-(?P<section>.+).xml$ , django.contrib.sitemaps.views.sitemap , { sitemaps : sitemaps})
152
Esto genera automticamente un chero sitemap.xml que hace referencia a ambos cheros sitemap-flatpages.xml y sitemap-blog.xml. La clase Sitemap y el diccionario sitemaps no cambian en absoluto.
Una solucin ms eciente, sin embargo, sera llamar a ping_google() desde un script cron o un manejador de tareas. La funcin hace un pedido HTTP a los servidores de Google, por lo que no querrs introducir esa demora asociada a la actividad de red cada vez que se llame al mtodo save().
12.7 Qu sigue?
A continuacin, seguiremos indagando ms profundamente en las herramientas internas que Django nos ofrece. El Captulo 12 examina todas las herramientas que necesitas para proveer sitios personalizados: sesiones, usuarios, y autenticacin. Adelante!
12.7. Qu sigue?
153
154
CAPTULO 13
Tenemos que confesar algo: hasta el momento hemos ignorado un aspecto absolutamente importante del desarrollo web. Hemos hecho la suposicin de que el trco que visita nuestra web est compuesto por una masa amorfa de usuarios annimos, que se precipitan contra nuestras cuidadosamente diseadas pginas. Esto no es verdad, claro. Los navegadores que consultan nuestras pginas tienen a personas reales detrs (la mayor parte del tiempo, al menos). Este es un hecho importantsimo y que no debemos ignorar: Lo mejor de Internet es que sirve para conectar personas, no mquinas. Si queremos desarrollar un sitio web realmente competitivo, antes o despus tendremos que plantearnos como tratar a las personas que estn detrs del navegador. Por desgracia, no es tan fcil como podra parecer. El protocolo HTTP se dise especcamente para que fuera un protocolo sin estado, es decir, que cada peticin y respuesta est totalmente aislada de las dems. No hay persistencia entre una peticin y la siguiente, y ninguno de los atributos de la peticin (direccin IP, identicador del agente, etc...) nos permite discriminar de forma segura y consistente las peticiones de una persona de las del resto. En este captulo aprenderemos como solucionar esta carencia de estados. Empezaremos al nivel ms bajo (cookies), e iremos ascendiendo hasta las herramientas de alto nivel que nos permitirn gestionar sesiones, usuarios y altas o inscripciones de los mismos.
13.1 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, y as fue como nacieron las cookies (literalmente galleta). Una cookie es una pequea cantidad de informacin que el servidor delega en el navegador, de forma que este la almacena. Cada vez que el cliente web solicita una pgina del servidor, se le enva de vuelta la cookie. Veamos con un poco ms de detalle el funcionamiento. Cuando abrimos nuestro navegador y escribimos google.com, el navegador enva una solicitud HTTP a Google que empieza ms o menos as:
GET / HTTP/1.1 Host: google.com ...
155
HTTP/1.1 200 OK Content-Type: text/html Set-Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671; expires=Sun, 17-Jan-2038 19:14:07 GMT; path=/; domain=.google.com Server: GWS/2.1 ...
Fjate en la lnea que comienza con Set-Cookie. El navegador almacenar el valor indicado (PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671) y se lo volver a enviar a Google cada vez que vuelva a acceder a alguna de sus pginas; de esa forma, la prxima vez que vuelvas a Google, la peticin que enviar el navegador se parecer a esta:
GET / HTTP/1.1 Host: google.com Cookie: PREF=ID=5b14f22bdaf1e81c:TM=1167000671:LM=1167000671 ...
Google puede saber ahora, gracias al valor de la Cookie, que eres la misma persona que accedi un rato antes. Este valor puede ser, por ejemplo, una clave en una tabla de la base de datos que almacene los datos del usuario. Con esa informacin, Google puede hacer aparecer tu nombre en la pgina (de hecho, lo hace).
Denir los valores de las cookies es slo un poco ms complicado. Debes usar el mtodo set_cookie() en un objeto de tipo HttpResponse. He aqu un ejemplo que dene la cookie favorite_color utilizando el valor que se le pasa como parmetro GET:
def set_color(request): if " favorite_color " in request.GET: # Create an HttpResponse object... response = HttpResponse( " Your favorite color is now request.GET[ " favorite_color " ]) # ... and set a cookie on the response response.set_cookie( " favorite_color " , request.GET[ " favorite_color " ]) return response
%s " % \
156
else: return HttpResponse( " You didn t give a favorite color. " )
Hay una serie de parmetros opcionales que puedes pasar a response.set_cookie() y que te permiten controlar determinadas caractersticas de la cookie, tal y como se muestra en la tabla 12-1. Parmetro Valor por omisin max_age None Descripcin
expires None
path
/"
domain None
secure False
El tiempo (en segundos) que la cookie debe permanecer activa. Si este parmetro es la cookie, desaparecer automticamente cuando se cierre el navegador. La fecha y hora en que la cookie debe expirar. Debe estar en el formato "Wdy, DD-Mth-YY HH:MM:SS GMT". Si se utiliza este parmetro, su valor tiene preferencia sobre el denido mediante max_age. La ruta o path para la cual es vlida la cookie. Los navegadores solo reenviarn la cookie a las pginas que estn en dicha ruta. Esto impide que se enve esta cookie a otras secciones de la web. Es especialmente til si no se tiene el control del nivel superior de directorios del servidor web. El dominio para el cual es vlida la cookie. Se puede usar este parmetro para denir una cookie que sea apta para varios dominios. Por ejemplo, deniendo domain=".example.com" la cookie ser enviada a los dominios www.example.com, www2.example.com y aun.otro.subdominio.example.com. Si a este parmetro no se le asigna ningn valor, la cookie solo ser enviada al dominio que la deni. Si este valor se dene como True, se le indica al navegador que slo retorne esta cookie a las pginas que se accedan de forma segura (protocolo HTTPS en vez de HTTP).
Las Cookies ni siquiera son seguras para los servidores. La mayora de los navegadores permiten manipular y editar de forma sencilla los contenidos de cookies individuales, y existen herramientas como mechanize (http://wwwsearch.sourceforge.net/mechanize/) que permiten a cualquiera que est lo sucientemente motivado construir solicitudes HTTP a mano. As que tampoco debemos almacenar en las cookies datos que sean fciles de falsicar. El error habitual en este escenario consiste en almacenar algo as como IsLoggedIn=1 en una cookie cuando el usuario se ha validado. Te sorprendera saber cuantos sitios web cometen este tipo de error; no lleva ms de unos segundos engaar a sus sistemas de seguridad.
2. Comprobar que django.contrib.sessions est incluido en el valor de INSTALLED_APPS (y ejecutar manage.py syncdb si lo tuviste que aadir). Los valores por defecto creados por startproject ya tienes estas dos caractersticas habilitadas, as que a menos que las hayas borrado, es muy probable que no tengas que hacer nada para empezar a usar las sesiones. Si lo que quieres en realidad es no usar sesiones, deberas quitar la referencia a SessionMiddleware de MIDDLEWARE_CLASSES y borrar django.contrib.sessions de INSTALLED_APPS. Esto te ahorrar slo un poco de sobrecarga, pero toda ayuda es buena.
158
# Check if the session has a given key: if " fav_color " in request.session: ...
Tambin puedes usar otros mtodos propios de un diccionario como keys() o items() en request.session. Hay dos o tres reglas muy sencillas para usar ecazmente las sesiones en Django: Debes usar slo cadenas de texto normales como valores de clave en request.session, en vez de, por ejemplo, enteros, objetos, etc. Esto es ms un convenio que un regla en el sentido estricto, pero merece la pena seguirla. Los valores de las claves de una sesin que empiecen con el carcter subrayado estn reservadas para uso interno de Django. En la prctica, slo hay unas pocas variables as, pero, a no ser que sepas lo que ests haciendo (y ests dispuesto a mantenerte al da en los cambios internos de Django), lo mejor que puedes hacer es evitar usar el carcter subrayado como prejo en tus propias variables; eso impedir que Django pueda interferir con tu aplicacin, Nunca reemplaces request.session por otro objeto, y nunca accedas o modiques sus atributos. Utilzalo slo como si fuera un diccionario. Veamos un ejemplo rpido. Esta vista simplicada dene una variable has_commented como True despus de que el usuario haya publicado un comentario. Es una forma sencilla (aunque no particularmente segura) de impedir que el usuario publique dos veces el mismo comentario:
def post_comment(request, new_comment): if request.session.get( has_commented , False): return HttpResponse( " You ve already commented. " ) c = comments.Comment(comment=new_comment) c.save() request.session[ has_commented ] = True return HttpResponse( Thanks for your comment! )
Esta vista simplicada permite que un usuario se identique como tal en nuestras pginas:
def login(request): try: m = Member.objects.get(username__exact=request.POST[ username ]) if m.password == request.POST[ password ]: request.session[ member_id ] = m.id return HttpResponse( " You re logged in. " ) except Member.DoesNotExist: return HttpResponse( " Your username and password didn t match. " )
Nota: En la prctica, esta sera una forma psima de validar a tus usuarios. El mecanismo de autenticacin que presentaremos un poco ms adelante realiza esta tarea de forma mucho ms segura y robusta. Los ejemplo son deliberadamente simples para que se comprendan con ms facilidad.
159
Nota De nuevo, las funciones de autenticacin ya denidas en el entorno realizan estos chequeos por vos.
160
Para poder acceder a los datos de la sesin, hay que usar el mtodo get_decoded(). Esto se debe a que estos datos, que consistan en un diccionario, estn almacenados codicados:
>>> s.session_data KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj... >>> s.get_decoded() {user_id: 42}
Se puede cambiar este comportamiento, especicando la opcin SESSION_SAVE_EVERY_REQUEST a True. Si lo hacemos as, Django almacenar la sesin en la base de datos en cada peticin, incluso si no se ha modicado ninguno de sus valores. Fjate que la cookie de sesin slo se enva cuando se ha creado o modicado una sesin. Si SESSION_SAVE_EVERY_REQUEST est como True, la cookie de sesin ser reenviada en cada peticin. De forma similar, la seccin de expiracin (expires) se actualizar cada vez que se reenve la cookie.
161
Opcin
Descripcin
SESSION_COOKIE_DOMAINa utilizar por la cookie de sesin. Se puede utilizar, por El Dominio ejemplo, el valor ".lawrence.com" para utilizar la cookie en diferentes subdominios. El valor None indica una cookie estndar. SESSION_COOKIE_NAME de la cookie de sesiones. Puede ser cualquier cadena de texto. El nombre SESSION_COOKIE_SECURE Indica si se debe usar una cookie segura para la cookie de sesin. Si el valor es True, la cookie se marcar como segura, lo que signica que slo se podr utilizar mediante el protocolo HTTPS. Detalles tcnicos
"sessionid" False
Para los ms curiosos, he aqu una serie de notas tcnicas acerca de algunos aspectos interesantes de la gestin interna de las sesiones: El diccionario de la sesin acepta cualquier objeto Python capaz de ser serializado con pickle. Vase la documentacin del mdulo pickle incluido en la biblioteca estndar de Python para ms informacin. Los datos de la sesin se almacenan en una tabla en la base de datos llamada django_session. Los datos de la sesin son suministrados bajo demanda. Si nunca accedes al atributo request.session, Django nunca acceder a la base de datos. Django slo enva la cookie si tiene que hacerlo. Si no modicas ningn valor de la sesin, no reenva la cookie (a no ser que hayas denido SESSION_SAVE_EVERY_REQUEST como True). El entorno de sesiones de Django se basa entera y exclusivamente en las cookies. No almacena la informacin de la sesin en las URL, como recurso extremo en el caso de que no se puedan utilizar las cookies, como hacen otros entornos (PHP, JSP). Esta es una decisin tomada de forma consciente. Poner los identicadores de sesin en las URL no solo hace que las direcciones sean ms feas, tambin hace que el sistema sea vulnerable ante un tipo de ataque en que se roba el identicador de la sesin utilizando la cabecera Referer. Si aun te pica la curiosidad, el cdigo fuente es bastante directo y claro, mira en django.contrib.sessions para ms detalles.
162
Permisos: Valores binarios (Si/No) que indican si un usuario puede o no realizar una tarea determinada. grupos: Una forma genrica de aplicar etiquetas y permisos a ms de un usuario. mensajes: Un mecanismo sencillo que permite enviar y mostrar mensajes del sistema usando una cola. Perles: Un mecanismo que permite extender los objetos de tipo usuario con campos adicionales. Si ya has utilizado la herramienta de administracin (descrita en el Captulo <chapter06>6), habrs visto muchas de estas utilidades, y si has modicado usuarios y grupos con dicha herramienta, ya has modicado las tablas en las que se basa el sistema aut/aut.
163
Campo Descripcin username Obligatorio; 30 caracteres como mximo. Slo acepta caracteres alfanumricos (letras, dgitos y el carcter subrayado). first_name pcional; 30 caracteres como mximo. O last_nameOpcional; 30 caracteres como mximo. email Opcional. Direccin de correo electrnico. password Obligatorio. Un cdigo de comprobacin (hash), junto con otros metadatos de la contrasea. Django nunca almacena la contrasea en crudo. Vase la seccin Cambia contraseas para ms informacin is_staff Booleano. Indica que el usuario puede acceder a las secciones de administracin. is_activeBooleano. Indica que la cuenta puede ser usada para identicarse. Se puede poner a False para deshabilitar a un usuario sin tener que borrarlo de la tabla. is_superuser Booleano. Seala que el usuario tiene todos los permisos, an cuando no se le hayan asignado explcitamente last_loginecha y hora de la ltima vez que el usuario se identic. Se asigna automticamente a F la fecha actual por defecto. date_joined Fecha y hora en que fue creada esta cuenta de usuario. Se asigna automticamente a la fecha actual en su momento. Metodos de los objetos User Mtodo Descripcin is_authenticated() Siempre devuelve True para usuario reales. Es una forma de determinar si el usuario se ha identicado. esto no implica que posea ningn permiso, y tampoco comprueba que la cuenta est activa. Slo indica que el usuario se ha identicado con xito. is_anonymous() Devuelve True slo para usuarios annimos, y False para usuarios reales. En general, es preferible usar el mtodo is_authenticated(). get_full_name() Devuelve la concatenacin de los campos first_name y last_name, con un espacio en medio. set_password(passwd) la contrasea del usuario a la cadena de texto en claro indicada, Cambia realizando internamente las operaciones necesarias para calcular el cdigo de comprobacin o hash necesario. Este mtodo no guarda el objeto User. check_password(passwd) True si la cadena de texto en claro que se le pasa coincide con la devuelve contrasea del usuario. Realiza internamente las operaciones necesarias para calcular los cdigos de comprobacin o hash necesarios. get_group_permissions() una lista con los permisos que tiene un usuario, obtenidos a travs Devuelve del grupo o grupos a las que pertenezca. get_all_permissions() una lista con los permisos que tiene concedidos un usuario, ya sea a Devuelve travs de los grupos a los que pertenece o bien asignados directamente. has_perm(perm) Devuelve True si el usuario tiene el permiso indicado. El valor de perm est en el formato "package.codename". Si el usuario no est activo, siempre devolver False. has_perms(perm_list) Devuelve True si el usuario tiene todos los permisos indicados. Si el usuario no est activo, siempre devolver False. has_module_perms(app_label) si el usuario tiene algn permiso en la etiqueta de aplicacin Devuelve True indicada, app_label. Si el usuario no est activo, siempre devolver False. get_and_delete_messages() lista de mensajes (objetos de la clase Message) de la cola del Devuelve una usuario, y los borra posteriormente. email_user(subj,Enva un correo electrnico al usuario. El mensaje aparece como enviado msg) desde la direccin indicada en el valor DEFAULT_FROM_EMAIL. Se le puede pasar un tercer parmetro opcional, from_email, para indicar otra direccin de remite distinta. get_profile() Devuelve un perl del usuario especco para el sitio. En la seccin Perles se explicar con ms detalle este mtodo. Por ltimo, los objetos de tipo User mantienen dos campos de relaciones mltiples o muchos-amuchos: Grupos y permisos (groups y permissions). Se puede acceder a estos objetos relacionados de la misma manera en que se usan otros campos mltiples: 164 Captulo 13. Captulo 12: Sesiones, usuario e inscripciones
# Set a users groups: myuser.groups = group_list # Add a user to some groups: myuser.groups.add(group1, group2,...) # Remove a user from some groups: myuser.groups.remove(group1, group2,...) # Remove a user from all groups: myuser.groups.clear() # Permissions work the same way myuser.permissions = permission_list myuser.permissions.add(permission1, permission2, ...) myuser.permissions.remove(permission1, permission2, ...) myuser.permissions.clear()
La llamada a authenticate() slo verica las credenciales del usuario. Todava hay que realizar una llamada a login() para completar el inicio de sesin. La llamada a login() acepta un objeto de la clase HttpRequest y un objeto User y almacena el identicador del usuario en la sesin, usando el entorno de sesiones de Django. El siguiente ejemplo muestra el uso de ambas funciones, authenticate() y login(), dentro de una vista:
from django.contrib import auth def login(request): username = request.POST[ username ] password = request.POST[ password ] user = auth.authenticate(username=username, password=password) if user is not None and user.is_active: # Correct password, and the user is marked "active" auth.login(request, user) # Redirect to a success page. return HttpResponseRedirect( " /account/loggedin/ " ) else: # Show an error page return HttpResponseRedirect( " /account/invalid/ " )
165
Para cerrar la sesin, se puede llamar a django.contrib.auth.logout() dentro de una vista. Necesita que se le pase como parmetro un objeto de tipo HttpRequest, y no devuelve ningn valor:
from django.contrib import auth def logout(request): auth.logout(request) # Redirect to a success page. return HttpResponseRedirect( " /account/loggedout/ " )
La llamada a logout() no produce ningn error, aun si no hubiera ningn usuario conectado. En la prctica, no es normalmente necesario escribir tus propias funciones para realizar estas tareas; el sistema de autenticacin viene con un conjunto de vistas predenidas para ello. El primer paso para utilizar las vistas de autenticacin es mapearlas en tu URLconf. Necesitas modicar tu cdigo hasta tener algo parecido a esto:
from django.contrib.auth.views import login, logout urlpatterns = patterns( , # existing patterns here... ( r ^accounts/login/$ , login), ( r ^accounts/logout/$ , logout), )
/accounts/login/ y /accounts/logout/ son las URL por defecto que usa Django para estas vistas. Por defecto, la vista de login utiliza la plantilla denida en registration/login.html (puedes cambiar el nombre de la plantilla utilizando un parmetro opcional, template_name). El formulario necesita contener un campo llamado username y otro llamado password. Una plantilla de ejemplo podra ser esta:
{% extends "base.html" %} {% block content %} {% if form.errors %} <p class= "error" >Sorry, thats not a valid username or password</p> {% endif %} <form action= . 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= . method= post > {% endblock %}
Si el usuario se identica correctamente, su navegador ser redirigido a /accounts/profile/. Puedes indicar una direccin distinta especicando un tercer campo (normalmente oculto) que se llame next, cuyo valor debe ser la URL a redireccionar despus de la identicacin. Tambin puedes pasar este valor como un parmetro GET a la vista de identicacin y se aadir automticamente su valor al contexto en una variable llamada next, que puedes incluir ahora en un campo oculto. La vista de cierre de sesin se comporta de forma un poco diferente. Por defecto utiliza la plantilla denida en registration/logged_out.html (que normalmente contiene un mensaje del tipo
166
Ha cerrado su sesin). No obstante, se puede llamar a esta vista con un parmetro extra, llamado next_page, que indicara la vista a la que se debe redirigir una vez efectuado el cierre de la sesin.
Si se desea abreviar, se puede usar el decorador login_required sobre las vistas que nos interese proteger:
from django.contrib.auth.decorators import login_required @login_required def my_view(request): # ...
Esto es lo que hace el decorador login_required: Si el usuario no est identicado, redirige a la direccin /accounts/login/, incluyendo la url actual como un parmetro con el nombre next, por ejemplo /accounts/login/?next=/polls/3/. Si el usuario est identicado, ejecuta la vista sin ningn cambio. La vista puede asumir sin problemas que el usuario esta identicado correctamente
De nuevo, Django proporciona una forma abreviada llamada user_passes_test. Requiere que se la pasen unos argumentos y genera un decorador especializado para cada situacin en particular:
167
def user_can_vote(user): return user.is_authenticated() and user.has_perm( " polls.can_vote " ) @user_passes_test(user_can_vote, login_url= " /login/ " ) def vote(request): # Code here can assume a logged-in user with the correct permission. ...
El decorador user_passes_test tiene un parmetro obligatorio: un objeto que se pueda llamar (normalmente una funcin) y que a su vez acepte como parmetro un objeto del tipo User, y devuelva True si el usuario puede acceder y False en caso contrario. Es importante destacar que user_passes_test no comprueba automticamente que el usuario est identicado; esa es una comprobacin que se debe hacer explcitamente. En este ejemplo, hemos usado tambin un segundo parmetro opcional, login_url, que te permite indicar la url de la pgina que el usuario debe utilizar para identicarse (/accounts/login/ por defecto). Comprobar si un usuario posee un determinado permiso es una tarea muy frecuente, as que Django proporciona una forma abreviada para estos casos: El decorador permission_required(). Usando este decorador, el ejemplo anterior se podra codicar as:
from django.contrib.auth.decorators import permission_required @permission_required(polls.can_vote, login_url="/login/") def vote(request): # ...
El decorador permission_required() tambin acepta el parmetro opcional login_url, de nuevo con el valor /accounts/login/ en caso de omisin. Limitar el acceso a vistas genricas Una de las preguntas ms frecuentes en la lista de usuarios de Django trata de cmo limitar el acceso a una vista genrica. Para conseguirlo, tienes que usar un recubrimiento sencillo alrededor de la vista que quieres proteger, y apuntar en tu URLconf al recubrimiento en vez de a la vista genrica:
from dango.contrib.auth.decorators import login_required from django.views.generic.list_detail import object_detail @login_required def limited_object_detail(*args, **kwargs): return object_detail(*args, **kwargs)
Puedes cambiar el decorador login_required por cualquier otro que quieras usar, como es lgico.
168
>>> from django.contrib.auth.models import User >>> user = User.objects.create_user(username= john , ... ... email= jlennon@beatles.com , password= glass onion )
En este momento, user es una instancia de la clase User, preparada para ser almacenada en la base de datos (create_user() no llama al mtodo save()). Este te permite cambiar algunos de sus atributos antes de guardarlos, si quieres:
>>> user.is_staff = True >>> user.save()
No debes modicar directamente el atributo password, a no ser que tengas muy claro lo que ests haciendo. La contrasea se almacena en la base de datos en forma de cdigo de comprobacin (salted hash) y, por tanto, debe ser modicada slo a travs de este mtodo. Para ser ms exactos, el atributo password de un objeto User es una cadena de texto con el siguiente formato:
hashtype$salt$hash
Es decir, el tipo de hash, el grano de sal (salt) y el cdigo hash propiamente dicho, separados entre si por el carcter dolar ($). El valor de hashtype puede ser sha1 (por defecto) o md5, el algoritmo usado para realizar una transformacin hash de un solo sentido sobre la contrasea. El grano de sal es una cadena de texto aleatoria que se utiliza para aumentar la resistencia de esta codicacin frente a un ataque por diccionario. Por ejemplo:
sha1$a1976$a36cc8cbf81742a8fb52e221aaeab48ed7f58ab4
Las funciones User.set_password() y User.check_password() manejan todos estos detalles y comprobaciones de forma transparente. Tengo que echar sal a mi ordenador? No, la sal de la que hablamos no tiene nada que ver con ninguna receta de cocina; es una forma habitual de aumentar la seguridad a la hora de almacenar una contrasea. Una funcin hash es una funcin criptogrca, que se caracteriza por ser de un solo sentido; es decir, es fcil calcular el cdigo hash de un determinado valor, pero es prcticamente imposible reconstruir el valor original partiendo nicamente del cdigo hash. Si almacenramos las contraseas como texto en claro, cualquiera que pudiera obtener acceso a la base de datos podra saber sin ninguna dicultad todas las contraseas al instante. Al guardar las contraseas en forma de cdigos hash se reduce el peligro en caso de que se comprometa la seguridad de la base de datos. No obstante, un atacante que pudiera acceder a la base de datos podra ahora realizar un ataque por fuerza bruta, calculando los cdigos hash de millones de contraseas distintas y comparando esos cdigos con los que estn almacenados en la base de datos. Este llevar algo de tiempo, pero menos de lo que parece, los ordenadores son increblemente rpidos.
169
Para empeorar las cosas, hay disponibles pblicamente lo que se conoce como tablas arco iris (rainbow tables), que consisten en valores hash precalculados de millones de contraseas de uso habitual. Usando una tabla arco iris, un atacante puede romper la mayora de las contraseas en segundos. Para aumentar la seguridad, se aade un valor inicial aleatorio y diferente a cada contrasea antes de obtener el cdigo hash. Este valor aleatorio es el grano de sal. Como cada grano de sal es diferente para cada password se evita el uso de tablas arco iris, lo que obliga al atacante a volver al sistema de ataque por fuerza bruta, que a su vez es ms complicado al haber aumentado la entropa con el grano de sal. Otra ventaja es que si dos usuarios eligen la misma contrasea, al aadir el grano de sal los cdigos hash resultantes sern diferentes. Aunque esta tcnica no es, en trminos absolutos, la ms segura posible, ofrece un buen compromiso entre seguridad y conveniencia.
El alta del usuario Podemos usar estas herramientas de bajo nivel para crear vistas que permitan al usuario darse de alta. Prcticamente todos los desarrolladores quieren implementar el alta del usuario a su manera, por lo que Django da la opcin de crearte tu propia vista para ello. Afortunadamente, es muy fcil de hacer. La forma ms sencilla es escribir una pequea vista que pregunte al usuario los datos que necesita y con ellos se cree directamente el usuario. Django proporciona un formulario prefabricado que se puede usar con este n, como se muestra en el siguiente ejemplo:
from from from from django import oldforms as forms django.http import HttpResponseRedirect django.shortcuts import render_to_response django.contrib.auth.forms import UserCreationForm
def register(request): form = UserCreationForm() if request.method == POST : data = request.POST.copy() errors = form.get_validation_errors(data) if not errors: new_user = form.save(data) return HttpResponseRedirect( " /books/ " ) else: data, errors = {}, {} return render_to_response( " registration/register.html " , { form : forms.FormWrapper(form, data, errors) })
Este formulario asume que existe una plantilla llamada registration/register.html. esa plantilla podra consistir en algo parecido a esto:
{% extends "base.html" %} {% block title %}Create an account{% endblock %} {% block content %} <h1>Create an account</h1> <form action= "." method= "post" > {% if form.error_dict %} <p class= "error" >Please correct the errors below.</p> {% endif %} {% if form.username.errors %} {{ form.username.html_error_list }}
170
{% endif %} <label for= "id_username" >Username:</label> {{ form.username }} {% if form.password1.errors %} {{ form.password1.html_error_list }} {% endif %} <label for= "id_password1" >Password: {{ form.password1 }} {% if form.password2.errors %} {{ form.password2.html_error_list }} {% endif %} <label for= "id_password2" >Password (again): {{ form.password2 }} <input type= "submit" value= "Create the account" /> </label> {% endblock %}
Nota: django.contrib.auth.forms.UserCreationForm es, a la hora de publicar esto, un formulario del estilo oldforms. Vase http://www.djangoproject.com/documentation/0.96/forms/ para ms detalles sobre oldforms. La transicin al nuevo sistema, tal y como se explica en el captulo siete, ser completada muy pronto.
Los permisos del usuario se almacenan en la variable {{ perms }}. En realidad, es una forma simplicada de acceder a un par de mtodos sobre los permisos que veremos en breve. Hay dos formas de usar este objeto perms. Puedes usar {{ perms.polls }} para comprobar si un usuario tienen algn permiso para una determinada aplicacin, o se puede usar una forma ms especca, como {{ perms.polls.can_vote }}, para comprobar si el usuario tiene concedido un permiso en concreto. Por lo tanto, se pueden usar estas comprobaciones en sentencias { % if %}:
{% if perms.polls %} <p>You have permission to do something in the polls app.</p> {% if perms.polls.can_vote %} <p>You can vote!</p> {% endif %}
171
{% else %} <p>You dont have permission to do anything in the polls app.</p> {% endif %}
13.5.1 Permisos
Los permisos son una forma sencilla de marcar que determinados usuarios o grupos pueden realizar una accin. Se usan normalmente para la parte de administracin de Django, pero puedes usarlos tambin en tu cdigo. El sistema de administracin de Django utiliza los siguientes permisos: Acceso a visualizar el formulario Aadir, y Aadir objetos, est limitado a los usuarios que tengan el permiso add para ese tipo de objeto. El acceso a la lista de cambios, ver el formulario de cambios y cambiar un objeto est limitado a los usuarios que tengan el permisos change para ese tipo de objeto. Borrar objetos est limitado a los usuarios que tengan el permiso delete para ese tipo de objeto. Los permisos se denen a nivel de las clases o tipos de objetos, no a nivel de instancias. Por ejemplo, se puede decir Mara puede modicar los reportajes nuevos, pero no Mara solo puede modicar los reportajes nuevos que haya creado ella, ni Mara slo puede cambiar los reportajes que tengan un determinado estado, fecha de publicacin o identicador. Estos tres permisos bsicos, aadir, cambiar y borrar, se crean automticamente para cualquier modelo Django que incluya una clase Admin. Entre bambalinas, los permisos se agregan a la tabla auth_permission cuando ejecutas manage.py syncdb. Estos permisos se crean con el siguiente formato: "<app>.<action>_<object_name>". Por ejemplo, si tienes una aplicacin llamada encuestas, con un modelo llamado Respuesta, se crearan automticamente los tres permisos con los nombres .encuestas.add_respuesta", .encuestas.change_respuesta" y .encuestas.delete_respuesta". Hay que tener cuidado de que el modelo tenga creada una clase Admin a la hora de ejecutar syncdb. Si no la tiene, no se crearn los permisos. Si has inicializado la base de datos y has aadido la clase Admin con posterioridad, debes ejecutar otra vez syncdb para crear los permisos. Tambin puedes denir tus propios permisos para un modelo, usando el atributo permissions en la clase Meta. El siguiente ejemplo crea tres permisos hechos a medida:
class USCitizen(models.Model): # ... class Meta: permissions = ( # Permission identifier ( " can_drive " , ( " can_vote " , ( " can_drink " , )
human-readable permission name " Can drive " ), " Can vote in elections " ), " Can drink alcohol " ),
Esto permisos slo se crearn cuando ejecutes syncdb. Es responsabilidad tuya comprobar estos permisos en tus vistas.
172
Igual que con los usuarios, los permisos se implementa en un modelo Django que reside en el mdulo django.contrib.auth.models. Esto signica que puedes usar la API de acceso a la base de datos para interactuar con los permisos de la forma que quieras.
13.5.2 Grupos
Los grupos son una forma genrica de trabajar con varios usuarios a la vez, de forma que se les pueda asignar permisos o etiquetas en bloque. Un usuario puede pertenecer a varios grupos a la vez. Un usuario que pertenezca a un grupo recibe automticamente todos los permisos que se la hayan otorgado al grupo. Por ejemplo, si el grupo Editores tiene el permiso can_edit_home_page, cualquier usuario que pertenezca a dicho grupo tambin tiene ese permiso. Los grupos tambin son una forma cmoda de categorizar a los usuarios para asignarles una determinada etiqueta, o para otorgarles una funcionalidad extra. Por ejemplo, se puede crear un grupo Usuarios especiales, y utilizar cdigo para permitir el acceso a determinadas porciones de tu sitio slo a los miembros de ese grupo, o para enviarles un correo electrnico slo a ellos. Al igual que con los usuarios, la manera ms sencilla de gestionar los grupos es usando la interfaz de administracin de Django. Los grupos, en cualquier caso, son modelos Django que residen en el mdulo django.contrib.auth.models as que, al igual que en el caso anterior, puedes usar la API de acceso a la base de datos para trabajar con los grupos a bajo nivel.
13.5.3 Mensajes
El sistema de mensajes es un forma muy ligera y sencilla de enviarle mensajes a un usuario. Cada usuario tiene asociada una cola de mensajes, de forma que los mensajes lleguen en el orden en que fueron enviados. Los mensajes no tienen ni fecha de caducidad ni fecha de envo. La interfaz de administracin de Django usa los mensajes para noticar que determinadas acciones han podido ser llevadas a cabo con xito. Por ejemplo, al crear un objeto, vers que aparece un mensaje en lo alto de la pgina de administracin, indicando que se ha podido crear el objeto sin problemas. Puedes usar la misma API para enviar o mostrar mensajes en tu propia aplicacin. Las llamadas de la API son bastante simples: Para crear un nuevo mensaje usa user.message_set.create(message=message_text). Para recuperar/eliminar mensajes usa user.get_and_delete_messages(), la cual retorna una lista de objetos Message en la cola del usuario (si es que existiera alguno) y elimina el mensaje de la misma. En el siguiente ejemplo, la vista guarda un mensaje para el usuario despus de crear una lista de reproduccin:
def create_playlist(request, songs): # Create the playlist with the given songs. # ... request.user.message_set.create( message= " Your playlist was added successfully. " ) return render_to_response( " playlists/create.html " , context_instance=RequestContext(request))
Al usar RequestContext, los mensajes del usuario actual, si los tuviera, estn accesibles desde la variable de contexto usando el nombre {{ messages }}. El siguiente ejemplo representa un fragmento de cdigo que muestras los mensajes:
{ % if messages %} <ul> { % for message in messages %} <li>{{ message }}</li>
173
Hay que hacer notar que RequestContext llama a get_and_delete_messages de forma implcita, por lo que los mensajes sern borrados, an si no se muestran en pantalla. Por ltimo, el sistema de mensajera slo funciona para usuarios de la base de datos. Para enviar mensajes a usuarios annimos hay que usar en entorno de sesiones directamente.
13.5.4 Perles
La parte nal de nuestro puzzle consiste en el sistema de perles. Para entender mejor que es este sistema y para que sirve, veamos primero el problema que se supone tiene que resolver. El resumen sera este: Muchos sitios en Internet necesitan almacenar ms informacin acerca de sus usuarios de la que est disponible en un objeto de la clase User. Para resolver este problema, otros entornos denen una serie de campos extra. Django, por el contrario, ha optado por proporcionar un mecanismo sencillo que permita crear un perl con los datos que queramos, y que queda enlazado con la cuenta de usuario. Estos perles pueden incluso ser diferentes segn cada proyecto, y tambin se pueden gestionar diferentes perles para sitios diferentes usando la misma base de datos. El primer paso para crear un perl es denir el modelo que va a almacenar la informacin que queremos guardar. El nico requerimiento que Django impone a este modelo es que disponga de una campo de tipo ForeignKey, que sea nico (unique=True) y que lo vincule con el modelo User. Adems, el campo debe llamarse user. Aparte de eso, puedes usar tantos y tan variados campos de datos como quieres. El siguiente ejemplo muestra un perl absolutamente arbitrario:
from django.db import models from django.contrib.auth.models import User class MySiteProfile(models.Model): # This is the only required field user = models.ForeignKey(User, unique=True) # The rest is completely up to you... favorite_band = models.CharField(maxlength=100, blank=True) favorite_cheese = models.CharField(maxlength=100, blank=True) lucky_number = models.IntegerField()
El siguiente paso es decirle a Django donde buscar estos perles. Para ello asigna a la variable de conguracin AUTH_PROFILE_MODULE el identicador de tu modelo. As, si el perl que denimos en el ejemplo anterior residiera en una aplicacin llamada myapp, pondras esto en tu chero de conguracin:
AUTH_PROFILE_MODULE = " myapp.mysiteprofile "
Una vez hecho esto, se puede acceder al perl del usuario llamando a user.get_profile(). Esta funcin elevar una excepcin de tipo DoesNotExist si el usuario no tiene creado el perl (puedes atrapar esta excepcin y crear el perl en ese momento).
13.6 Qu sigue?
Si, la verdad es que el sistema de autorizacin tiene tela que cortar. La mayor parte de las veces no tendrs que preocuparte por todos los detalles que se describen en este captulo, pero si alguna vez tienes que gestionar interacciones complicadas con los usuarios, agradecers tener todas las utilidades posibles a mano.
174
En el prximo captulo, echaremos un vistazo a una parte de Django que necesita la infraestructura que proporciona el sistema de usuarios/sesiones de Django: la aplicacin de comentarios. Esta aplicacin permite aadir, de forma muy sencilla, un completo sistema de comentarios -por parte de usuarios annimos o identicados- a cualquier tipo de objeto que queramos. Hasta el innito y ms all!
13.6. Qu sigue?
175
176
CAPTULO 14
Los sitios Web estticos, en las que las pginas son servidas directamente a la Web, generan un gran escalamiento. Una gran desventaja en los sitios Web dinmicos, es precisamente eso, que son dinmicos. Cada vez que un usuario pide una pgina, el servidor realiza una serie de clculosconsultas a una base de datos, renderizado de plantillas, lgica de negociopara crear la pgina que el visitante nalmente ve. Esto es costoso desde el punto de vista del sobreprocesamiento. Para la mayora de las aplicaciones Web, esta sobrecarga no es gran cosa. La mayora de las aplicaciones Web no son el washingtonpost.com o Slashdot; son de un tamao pequeo a uno mediano, y con poco trco. Pero para los sitios con trco de medio a alto es esencial bajar lo ms que se pueda el costo de procesamiento. He aqu cuando realizar un cache es de mucha ayuda. Colocar en cache algo signica guardar el resultado de un clculo costoso para que no se tenga que realizar el mismo la prxima vez. Aqu mostramos un pseudocdigo explicando como podra funcionar esto para una pgina Web dinmica:
dada una URL, buscar esa pgina en la cache si la pgina est en la cache: devolver la pgina en cache si no: generar la pgina guardar la pgina generada en la cache (para la prxima vez) devolver la pgina generada
Django incluye un sistema de cache robusto que permite guardar pginas dinmicas para que no tengan que ser recalculadas cada vez que se piden. Por conveniencia, Django ofrece diferentes niveles de granularidad de cache. Puedes dejar en cache el resultado de diferentes vistas, slo las piezas que son difciles de producir, o se puede dejar en cache el sitio entero. Django tambin trabaja muy bien con caches de upstream, tales como Squid (http://www.squidcache.org/) y las caches de los navegadores. Estos son los tipos de cache que no controlas directamente pero a las cuales puedes proveerles algunas pistas (va cabeceras HTTP) acerca de qu partes de tu sitio deben ser colocadas en cache y cmo. Sigue leyendo para descubrir como usar el sistema de cache de Django. Cuando tu sitio se parezca cada vez ms a Slashdot, estars contento de entender este material.
177
ms rpidos que otros). La cache en memoria generalmente ser mucho ms rpida que la cache en el sistema de archivos o la cache en una base de datos, porque carece del trabajo de tocar los mismos. Tus preferencias acerca de la cache van en CACHE_BACKEND en tu archivo de conguracin. Si usas cache y no especicas CACHE_BACKEND, Django usar simple:/// por omisin. Las siguientes secciones explican todos los valores disponibles para CACHE_BACKEND.
14.1.1 Memcached
Por lejos la ms rpida, el tipo de cache ms eciente para Django, Memcached es un framework de cache enteramente en memoria, originalmente desarrollado para manejar grandes cargas en LiveJournal (http://www.livejournal.com/) y subsecuentemente por Danga Interactive (http://danga.com/). Es usado por sitios como Slashdot y Wikipedia para reducir el acceso a bases de datos e incrementar el rendimiento dramticamente. Memcached est libremente disponible en http://danga.com/memcached/. Corre como un demonio y se le asigna una cantidad especca de memoria RAM. Su caracterstica principal es proveer una interfazuna super-liviana-y-rpida interfazpara aadir, obtener y eliminar arbitrariamente datos en la cache. Todos los datos son guardados directamente en memoria, por lo tanto no existe sobrecarga de uso en una base de datos o en el sistema de archivos. Despus de haber instalado Memcached, es necesario que instales los bindings Python para Memcached, los cuales no vienen con Django. Dichos bindings vienen en un mdulo de Python, memcache.py, el cual est disponible en http://www.tummy.com/Community/software/python-memcached/. Para usar Memcached con Django, coloca CACHE_BACKEND como memcached://ip:puerto/, donde ip es la direccin IP del demonio de Memcached y puerto es el puerto donde Memcached est corriendo. En el siguiente ejemplo, Memcached est corriendo en localhost (127.0.0.1) en el puerto 11211:
CACHE_BACKEND = memcached://127.0.0.1:11211/
Una muy buena caracterstica de Memcached es su habilidad de compartir la cache en varios servidores. Esto signica que puedes correr demonios de Memcached en diferentes mquinas, y el programa seguir tratando el grupo de diferentes mquinas como una sola cache, sin la necesidad de duplicar los valores de la cache en cada mquina. Para sacar provecho de esta caracterstica con Django, incluye todas las direcciones de los servidores en CACHE_BACKEND, separados por punto y coma. En el siguiente ejemplo, la cache es compartida en varias instancias de Memcached en las direcciones IP 172.19.26.240 y 172.19.26.242, ambas en el puerto 11211:
CACHE_BACKEND = memcached://172.19.26.240:11211;172.19.26.242:11211/
En el siguiente ejemplo, la cache es compartida en diferentes instancias de Memcached corriendo en las direcciones IP 172.19.26.240 (puerto 11211), 172.19.126.242 (puerto 11212) y 172.19.26.244 (puerto 11213):
CACHE_BACKEND = memcached://172.19.26.240:11211;172.19.26.242:11212;172.19.26.244:11213/
Una ltima observacin acerca de Memcached es que la cache basada en memoria tiene una importante desventaja. Como los datos de la cache son guardados en memoria, sern perdidos si los servidores se caen. Ms claramente, la memoria no es para almacenamiento permanente, por lo tanto no te quedes solamente con una cache basada en memoria. Sin duda, ninguno de los sistemas de cache de Django debe ser utilizado para almacenamiento permanenteson todos una solucin para la cache, no para almacenamientopero hacemos hincapi aqu porque la cache basada en memoria es particularmente temporaria.
178
Donde [nombre_tabla_cache] es el nombre de la tabla a crear. Este nombre puede ser cualquiera que desees, siempre y cuando sea un nombre vlido para una tabla y que no est ya en uso en tu base de datos. Este comando crea una nica tabla en tu base de datos con un formato apropiado para el sistema de cache de Django. Una vez que se hayas creado la tabla, coloca la propiedad CACHE_BACKEND como "db://nombre_tabla", donde nombre_tabla es el nombre de la tabla en la base de datos. En el siguiente ejemplo, el nombre de la tabla para el cache es mi_tabla_cache:
CACHE_BACKEND = db://mi_tabla_cache
El sistema de cache usar la misma base de datos especicada en el archivo de conguracin. No podrs usar una base de datos diferente para tal.
Observa que hay tres barras invertidas en el comienzo del ejemplo anterior. Las primeras dos son para file://, y la tercera es el primer caracter de la ruta del directorio, /var/tmp/django_cache. Si ests en Windows, coloca la letra correspondiente al disco despus de file://, como aqu:: file://c:/foo/bar. La ruta del directorio debe ser absolutadebe comenzar con la raz de tu sistema de archivos. No importa si colocas una barra al nal de la misma. Asegrate que el directorio apuntado por esta propiedad exista y que pueda ser ledo y escrito por el usuario del sistema usado por tu servidor Web para ejecutarse. Continuando con el ejemplo anterior, si tu servidor corre como usuario apache, asegrate que el directorio /var/tmp/django_cache exista y pueda ser ledo y escrito por el usuario apache. Cada valor de la cache ser almacenado como un archivo separado conteniendo los datos de la cache serializados (pickled), usando el mdulo Python pickle. Cada nombre de archivo es una clave de la cache, modicado convenientemente para que pueda ser usado por el sistema de archivos.
179
Esta cache apenas guarda los datos en proceso, lo que signica que slo debe ser usada para desarrollo o testing.
Como resultado de esto, tu entorno de desarrollo no usar cache, pero tu entorno de produccin si lo har.
Tanto los argumentos desconocidos asi como los valores invlidos de argumentos conocidos son ignorados silenciosamente.
180
Nota: El orden de MIDDLEWARE_CLASSES importa. Mira la seccin Orden de MIDDLEWARE_CLASSES ms adelante en este captulo. Luego, agrega las siguientes propiedades en el archivo de conguracin de Django: CACHE_MIDDLEWARE_SECONDS: El tiempo en segundos que cada pgina ser mantenida en la cache. CACHE_MIDDLEWARE_KEY_PREFIX: Si la cache es compartida a travs de mltiples sitios usando la misma instalacin Django, coloca esta propiedad como el nombre del sitio, u otra cadena que sea nica para la instancia de Django, para prevenir colisiones. Usa una cadena vaca si no te interesa. La cache middleware coloca en cache cada pgina que no tenga parmetros GET o POST. Esto signica que si un usuario pide una pgina y pasa parmetros GET en la cadena de consulta, o pasa parmetros POST, la cache middleware no intentar obtener la versin en cache de la pgina. Si intentas usar la cache por sitio ten esto en mente cuando disees tu aplicacin; no uses URLs con cadena de consulta, por ejemplo, a menos que sea aceptable que tu aplicacin no coloque en cache esas pginas. Esta cache middleware admite otras caracterstica, CACHE_MIDDLEWARE_ANONYMOUS_ONLY. Si denes esta caracterstica, y la denes como True, la cache middleware slo colocar en cache pedidos annimos (p.e.: pedidos hechos por un usuario no logueado). Esta es una manera simple y efectiva de deshabilitar la cache para cualquier pgina de algn usuario especco, como la interfaz de administracin de Django. Ten en cuenta que si usas CACHE_MIDDLEWARE_ANONYMOUS_ONLY, debers asegurarte que has activado AuthenticationMiddleware y que AuthenticationMiddleware aparezca antes de CacheMiddleware en tus MIDDLEWARE_CLASSES Finalmente, nota que CacheMiddleware automticamente coloca unos pocos encabezados en cada HttpResponse: Coloca el encabezado Last-Modified con el valor actual de la fecha y hora cuando una pgina (an no en cache) es requerida. Coloca el encabezado Expires con el valor de la fecha y hora ms el tiempo denido en CACHE_MIDDLEWARE_SECONDS. Coloca el encabezado Cache-Control para otorgarle una vida mxima a la pgina, como se especica en CACHE_MIDDLEWARE_SECONDS.
181
los pedidos con parmetros GET y POST). Se aplica a cualquier vista que tu especiques, en vez de aplicarse al sitio entero. Haz esto usando un decorador, que es un wrapper de la funcin de la vista que altera su comportamiento para usar la cache. El decorador de cache por vista es llamado cache_page y se encuentra en el mdulo django.views.decorators.cache, por ejemplo:
from django.views.decorators.cache import cache_page def my_view(request, param): # ... my_view = cache_page(my_view, 60 * 15)
De otra manera, si ests usando la versin 2.4 o superior de Python, puedes usar la sintaxis de un decorador. El siguiente ejemplo es equivalente al anterior:
from django.views.decorators.cache import cache_page @cache_page(60 * 15) def my_view(request, param): # ...
cache_page recibe un nico argumento: el tiempo de vida en segundos de la cache. En el ejemplo anterior, el resultado de my_view() estar en cache unos 15 minutos. (toma nota de que lo hemos escrito como 60 * 15 para que sea entendible. 60 * 15 ser evaluado como 900que es igual a 15 minutos multiplicados por 60 segundos cada minuto.) La cache por vista, como la cache por sitio, es indexada independientemente de la URL. Si mltiples URLs apuntan a la misma vista, cada URL ser puesta en cache separadamente. Continuando con el ejemplo de my_view, si tu URLconf se ve como:
urlpatterns = ( , ( r ^foo/( \ d{1,2})/$ , my_view), )
los pedidos a /foo/1/ y a /foo/23/ sern puestos en cache separadamente, como es de esperar. Pero una vez que una misma URL es pedida (p.e. /foo/23/), los siguientes pedidos a esa URL utilizarn la cache.
182
El argumento timeout_seconds es opcional y obtiene el valor del argumento timeout de CACHE_BACKEND, explicado anteriormente, si no se lo especica. Si el objeto no existe en la cache, o el sistema de cache no se puede alcanzar, cache.get() devuelve None:
# Wait 30 seconds for my_key to expire... >>> cache.get( my_key ) None >>> cache.get( some_unset_key ) None
Te recomendamos que no almacenes el valor literal None en la cache, porque no podrs distinguir entre tu valor None almacenado y el valor que devuelve la cache cuando no encuentra un objeto. cache.get() puede recibir un argumento por omisin. Esto especica qu valor debe devolver si el objeto no existe en la cache:
>>> cache.get( my_key , has expired ) has expired
Para obtener mltiples valores de la cache de una sola vez, usa cache.get_many(). Si al sistema de cache le es posible, get_many() tocar la cache slo una vez, al contrario de tocar la cache por cada valor. get_many() devuelve un diccionario con todas las key que has pedido que existen en la cache y todava no han expirado:
183
Si una key no existe o ha expirado, no ser incluida en el diccionario. Lo siguiente es una continuacin del ejemplo anterior:
>>> cache.get_many([ a , b , c , d ]) {a: 1, b: 2, c: 3}
Finalmente, puedes eliminar keys explcitamente con cache.delete(). Esta es una manera fcil de limpiar la cache para un objeto en particular:
>>> cache.delete( a )
cache.delete() no tiene un valor de retorno, y funciona de la misma manera si existe o no un valor en la cache.
184
En este caso, el mecanismo de cache (como middleware) colocar en cache una versin distinta de la pgina para cada tipo de user-agent. La ventaja de usar el decorador vary_on_headers en vez de jar manualmente el encabezado Vary (usando algo como response[Vary] = user-agent) es que el decorador agrega al encabezado Vary (el cual podra ya existir), en vez de jarlo desde cero y potencialmente sobrescribir lo que ya haba ah. Puedes pasar mltiples encabezados a vary_on_headers():
@vary_on_headers( User-Agent , Cookie ) def my_view(request): # ...
Esto le dice a la cache de upstream que diferencie ambos, lo que signica que cada combinacin de una cookie y un navegador obtendr su propio valor en cache. Por ejemplo, un pedido con navegador Mozilla y una cookie con el valor foo=bar ser considerada diferente a un pedido con el navegador Mozilla y una cookie con el valor foo=ham. Como las variaciones con las cookies son tan comunes existe un decorador vary_on_cookie. Las siguientes dos vistas son equivalentes:
@vary_on_cookie def my_view(request): # ... @vary_on_headers( Cookie ) def my_view(request): # ...
El encabezado que le pasas a vary_on_headers no diferencia maysculas de minsculas; ser-Agent" es lo mismo que ser-agent". Tambin puedes usar django.utils.cache.patch_vary_headers como funcin de ayuda. Esta funcin ja o aade al Vary header, por ejemplo:
from django.utils.cache import patch_vary_headers
185
def my_view(request): # ... response = render_to_response( template_name , context) patch_vary_headers(response, [ Cookie ]) return response
patch_vary_headers obtiene una instancia de HttpResponse como su primer argumento y una lista/tupla de nombres de encabezados, sin diferenciar maysculas de minsculas, como su segundo argumento.
Este decorador se encarga de enviar los encabezados HTTP apropiados detrs de escena. Existen otras pocas maneras de controlar los parmetros de cache. Por ejemplo, HTTP permite a las aplicaciones hacer lo siguiente: Denir el tiempo mximo que una pgina debe estar en cache. Especicar si una cache debera comprobar siempre la existencia de nuevas versiones, entregando unicamente el contenido de la cache cuando no hubiesen cambios. (Algunas caches pueden entregar contenido aun si la pgina en el servidor ha cambiado, simplemente porque la copia en cache todava no ha expirado.) En Django, utiliza el decorador cache_control para especicar estos parmetros de la cache. En el siguiente ejemplo, cache_control le indica a la cache revalidarse en cada acceso y almacenar versiones en cache hasta 3.600 segundos:
from django.views.decorators.cache import cache_control @cache_control(must_revalidate=True, max_age=3600) def my_view(request): ...
Cualquier directiva Cache-Control de HTTP vlida es vlida en cache_control(). Aqu hay una lista completa: public=True private=True no_cache=True no_transform=True must_revalidate=True
186
proxy_revalidate=True max_age=num_seconds s_maxage=num_seconds Tip Para una explicacin de las directivas Cache-Control de HTTP, lea las especicaciones en http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9. El middleware de caching ya ja el encabezado max-age con el valor de CACHE_MIDDLEWARE_SETTINGS. Si utilizas un valor propio de max_age en un decorador cache_control, el decorador tendr precedencia, y los valores del encabezado sern fusionados correctamente.
14.8 Qu sigue?
Django incluye un nmero de paquetes opcionales. Hemos cubierto algunos de los mismos: el sistema de administracin (Captulo 6) y el marco de sesiones/usuarios (Captulo 11). El prximo captulo cubre el resto de los marcos de trabajos de la comunidad. Existen una cantidad interesante de herramientas disponibles; no querrs perderte ninguna de ellas.
187
188
CAPTULO 15
Una de las varias fortalezas de Python, es su losofa de bateras incluidas. Cuando instalas Python, viene con una amplia biblioteca de paquetes que puedes comenzar a usar inmediatamente, sin necesidad de descargar nada ms. Django trata de seguir esta losofa, e incluye su propia biblioteca estndar de agregados tiles para las tareas comunes del desarrollo web. Este captulo cubre dicha coleccin de agregados.
189
flatpages: un framework para administrar contenido HTML simple, plano, dentro de la base de datos. Consulta la seccin titulada Flatpages ms adelante. humanize: un conjunto de ltros de plantillas Django, tiles para darle un toque de humanidad a los datos. Consulta la seccin titulada Haciendo los datos ms humanos_ ms adelante. markup: un conjunto de ltros de plantillas de Django, que implementan varios lenguajes de marcado conocidos. Consulta la seccin titulada Filtros de marcado ms adelante. redirects: un framework para administrar redirecciones. Consulta la seccin titulada Redirects ms adelante. sessions: el framework de sesiones de Django. Consulta el Captulo 12. sitemaps: un framework para generara archivos de mapas de sitio XML. Consulta el Captulo 11_. sites: un framework que te permite operar mltiples sitios web desde la misma base de datos, y con una nica instalacin de Django. Consulta la prxima seccin, Sites. syndication: un framework para generar documentos de sindicacin (feeds), en RSS y en Atom. Consulta el Captulo 11_. El resto de este captulo entra en los detalles de cada paquete django.contrib que no ha sido cubierto an en este libro.
15.2 Sites
El sistema sites de Django es un framework genrico que te permite operar mltiples sitios web desde la misma base de datos, y desde el mismo proyecto de Django. ste es un concepto abstracto, y puede ser difcil de entender, as que comenzaremos mostrando algunos escenarios en donde sera til usarlo.
190
ser distinta para cada sitio. Empleando objetos Site, podemos abstraer el agradecimiento para usar los valores del nombre y dominio del sitio, variables name (ej. LJWorld.com) y domain (ej. www.ljworld.com). El framework sites te proporciona un lugar para que puedas almacenar el nombre (name) y el dominio (domain) de cada sitio de tu proyecto, lo que signica que puedes reutilizar estos valores de manera genrica.
Esa es toda la infraestructura necesaria para asociar artculos con mltiples sitios en tu base de datos. Con eso en su lugar, puedes reusar el mismo cdigo de vista para mltiples sitios. Continuando con el modelo Article del ejemplo, aqu mostramos cmo lucira una vista article_detail:
from django.conf import settings def article_detail(request, article_id): try: a = Article.objects.get(id=article_id, sites__id=settings.SITE_ID) except Article.DoesNotExist:
15.2. Sites
191
esta funcin de vista es reusable porque chequea el sitio del artculo dinmicamente, segn cul sea el valor de la opcin SITE_ID. Por ejemplo, digamos que el archivo de conguracin de LJWorld.com tiene un SITE_ID asignado a 1, y que el de Lawrence.com lo tiene asignado a 2. Si esta vista es llamada cuando el archivo de conguracin de LJWorld.com est activado, entonces la bsqueda de artculos se limita a aquellos en que la lista de sitios incluye LJWorld.com. Asociacin de contenido con un solo sitio De manera similar, puedes asociar un modelo con el modelo Site en una relacin muchos-a-uno, usando ForeignKey. Por ejemplo, si un artculo slo se permite en un sitio, puedes usar un modelo como este:
from django.db import models from django.contrib.sites.models import Site class Article(models.Model): headline = models.CharField(maxlength=200) # ... site = models.ForeignKey(Site)
Este tiene los mismos benecios, como se describe en la ltima seccin. Obtencin del sitio actual desde las vistas A un nivel ms bajo, puedes usar el framework sites en tus vistas de Django para hacer cosas particulares segn el sitio en el cual la vista sea llamada. Por ejemplo:
from django.conf import settings def my_view(request): if settings.SITE_ID == 3: # Do something. else: # Do something else.
Por supuesto, es horrible meter en el cdigo el ID del sitio de esa manera. Una forma levemente ms limpia de lograr lo mismo, es chequear el dominio actual del sitio:
from django.conf import settings from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get(id=settings.SITE_ID) if current_site.domain == foo.com: # Do something else: # Do something else.
Este fragmento de cdigo usado para obtener el objeto Site segn el valor de settings.SITE_ID es tan usado, que el administrador de modelos de Site (Site.objects) tiene un mtodo get_current(). El siguiente ejemplo es equivalente al anterior:
from django.contrib.sites.models import Site def my_view(request): current_site = Site.objects.get_current()
192
Obtencin del dominio actual para ser mostrado Una forma DRY (acrnimo del ingls Dont Repeat Yourself, no te repitas) de guardar el nombre del sitio y del dominio, como explicamos en Escenario 2: alojamiento del nombre/dominio de tu sitio en un solo lugar, se logra simplemente haciendo referencia a name y a domain del objeto Site actual. Por ejemplo:
from django.contrib.sites.models import Site from django.core.mail import send_mail def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... current_site = Site.objects.get_current() send_mail( Thanks for subscribing to %s alerts % current_site.name, Thanks for your subscription. We appreciate it. \n \n -The editor@ %s % current_site.domain, [user_email]) # ... %s
team. % current_site
Continuando con nuestro ejemplo de LJWorld.com y Lawrence.com, en Lawrence.com el correo electrnico tiene como sujeto la lnea Gracias por suscribirse a las alertas de lawrence.com. En LJWorld.com, en cambio, el sujeto es Gracias por suscribirse a las alertas de LJWorld.com. Este comportamiento especco para cada sitio, tambin se aplica al cuerpo del correo electrnico. Una forma an ms exible (aunque un poco ms pesada) de hacer lo mismo, es usando el sistema de plantillas de Django. Asumiendo que Lawrence.com y LJWorld.com tienen distintos directorios de plantillas (TEMPLATE_DIRS), puedes simplemente delegarlo al sistema de plantillas as:
from django.core.mail import send_mail from django.template import loader, Context def register_for_newsletter(request): # Check form values, etc., and subscribe the user. # ... subject = loader.get_template( alerts/subject.txt ).render(Context({})) message = loader.get_template( alerts/message.txt ).render(Context({})) send_mail(subject, message, do-not-reply@example.com , [user_email]) # ...
En este caso, debes crear las plantillas subject.txt y message.txt en ambos directorios de plantillas, el de LJWorld.com y el de Lawrence.com . Como mencionamos anteriormente, eso te da ms exibilidad, pero tambin es ms complejo. Una buena idea es explotar los objetos Site lo ms posible, para que no haya una complejidad y una redundancia innecesarias. Obtencin del dominio actual para las URLs completas La convencin de Django de usar get_absolute_url() para obtener las URLs de los objetos sin el dominio, est muy bien. Pero en en algunos casos puedes querer mostrar la URL completa con
15.2. Sites
193
http:// y el dominio, y todo para un objeto. Para hacerlo, puedes usar el framework sites. Este es un ejemplo:
>>> from django.contrib.sites.models import Site >>> obj = MyModel.objects.get(id=3) >>> obj.get_absolute_url() /mymodel/objects/3/ >>> Site.objects.get_current().domain example.com >>> http:// %s %s % (Site.objects.get_current().domain, obj.get_absolute_url()) http://example.com/mymodel/objects/3/
15.2.5 CurrentSiteManager
Si los Site juegan roles importantes en tu aplicacin, considera el uso del til CurrentSiteManager en tu modelo (o modelos). Es un administrador de modelos (consulta el Apndice B) que ltra automticamente sus consultas para incluir slo los objetos asociados al Site actual. Usa CurrentSiteManager agregndolo a tu modelo explcitamente. Por ejemplo:
from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to= /home/photos ) photographer_name = models.CharField(maxlength=100) pub_date = models.DateField() site = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager()
Con este modelo, Photo.objects.all() retorna todos los objetos Photo de la base de datos, pero Photo.on_site.all() retorna slo los objetos Photo asociados con el sitio actual, de acuerdo a la opcin de conguracin SITE_ID. En otras palabras, estas dos sentencias son equivalentes:
Photo.objects.filter(site=settings.SITE_ID) Photo.on_site.all()
Cmo supo CurrentSiteManager cul campo de Photo era el Site? Por defecto busca un campo llamado site. Si tu modelo tiene un campo ForeignKey o un campo ManyToManyField llamado de otra forma que site, debes pasarlo explcitamente como el parmetro para CurrentSiteManager. El modelo a continuacin, que tiene un campo llamado publish_on, lo demuestra:
from django.db import models from django.contrib.sites.models import Site from django.contrib.sites.managers import CurrentSiteManager class Photo(models.Model): photo = models.FileField(upload_to= /home/photos ) photographer_name = models.CharField(maxlength=100) pub_date = models.DateField() publish_on = models.ForeignKey(Site) objects = models.Manager() on_site = CurrentSiteManager( publish_on )
Si intentas usar CurrentSiteManager y pasarle un nombre de campo que no existe, Django lanzar un ValueError.
194
Nota: Probablemente querrs tener un Manager normal (no especco al sitio) en tu modelo, incluso si usas CurrentSiteManager. Como se explica en el Apndice B, si denes un manager manualmente, Django no crear automticamente el manager objects = models.Manager(). Adems, algunas partes de Django el sitio de administracin y las vistas genricas usan el manager que haya sido denido primero en el modelo. As que si quieres que el sitio de administracin tenga acceso a todos los objetos (no slo a los especcos al sitio actual), pon un objects = models.Manager() en tu modelo, antes de denir CurrentSiteManager.
15.3 Flatpages
A menudo tendrs una aplicacin Web impulsada por bases de datos ya funcionando, pero necesitars agregar un par de pginas estticas, tales como una pgina Acerca de o una pgina de Poltica de Privacidad. Sera posible usar un servidor Web estndar como por ejemplo Apache para servir esos archivos como archivos HTML planos, pero eso introduce un nivel extra de complejidad en tu aplicacin, porque entonces tienes que preocuparte de la conguracin de Apache, tienes que preparar el acceso para que tu equipo pueda editar esos archivos, y no puedes sacar provecho del sistema de plantillas de Django para darle estilo a las pginas. La solucin a este problema es la aplicacin atpages de Django, la cual reside en el paquete django.contrib.flatpages. Esta aplicacin te permite manejar esas pginas aisladas mediante el sitio de administracin de Django, y te permite especicar plantillas para las mismas usando el sistema de plantillas de Django. Detrs de escena usa modelos Django, lo que signica que almacena las pginas en una base de datos, de la misma manera que el resto de tus datos, y puedes acceder a las atpages con la API de bases de datos estndar de Django.
15.3. Flatpages
195
Las atpages son identicadas por su URL y su sitio. Cuando creas una atpage, especicas con cual URL est asociada, junto con en cul(es) sitio(s) est (para ms informacin acerca de sitios, consulta la seccin Sites).
FlatPage,
denido
en
Examinemos cada uno de los campos: url: La URL donde reside esta atpage, excluyendo el nombre del dominio pero incluyendo la barra (/) inicial (por ej. /about/contact/). title: El ttulo de la atpage. El framework no usa esto para nada en especial. Es tu responsabilidad visualizarlo en tu plantilla. content: El contenido de la atpage (por ej. el HTML de la pgina). El framework no usa esto para nada en especial. Es tu responsabilidad visualizarlo en tu plantilla. enable_comments: Indica si deben activarse los comentarios e esta atpage. El framework no usa esto para nada en especial. Puedes comprobar este valor en tu plantilla y mostrar un formulario de comentario si es necesario. template_name: El nombre de la plantilla a usarse para renderizar esta atpage. Es opcional; si no se indica o si esta plantilla no existe, el framework usar la plantilla flatpages/default.html. registration_required: Indica si se requerir registro para ver esta atpage. Esto se integra con el framework de autenticacin/usuarios de Django, el cual se trata en el Captulo 12. sites: Los sitios en los cuales reside esta atpage. Esto se integra con el framework sites de Django, el cual se trata en la seccin Sites en este captulo.
196
Puedes crear atpages ya sea a travs de la interfaz de administracin de Django o a travs de la API de base de datos de Django. Para ms informacin, examina la seccin Agregando, modicando y eliminando atpages. Una vez que has creado atpages, FlatpageFallbackMiddleware se encarga de todo el trabajo. Cada vez que cualquier aplicacin Django lanza un error, este middleware verica como ltimo recurso la base de datos de atpages en bsqueda de la URL que se ha requerido. Especcamente busca una atpage con la URL en cuestin y con un identicador de sitio que coincida con la variable de conguracin SITE_ID. Si encuentra una coincidencia, carga la plantilla de la atpage, o flatpages/default.html si la atpage no ha especicado una plantilla personalizada. Le pasa a dicha plantilla una nica variable de contexto: flatpage, la cual es el objeto atpage. Usa RequestContext para renderizar la plantilla. Si FlatpageFallbackMiddleware no encuentra una coincidencia, el proceso de la peticin contina normalmente. Nota: Este middleware slo se activa para errores 404 (pgina no encontrada) no para errores 500 (error en servidor) u otras respuestas de error. Nota tambin que el orden de MIDDLEWARE_CLASSES es relevante. Generalmente, puedes colocar el FlatpageFallbackMiddleware cerca o en el nal de la lista, debido a que se trata de una opcin de ltimo recurso.
15.3. Flatpages
197
15.4 Redirects
El framework redirects de Django te permite administrar las redirecciones con facilidad almacenndolos en una base de datos y tratndolos como cualquier otro objeto modelo de Django. Por ejemplo puedes usar el framework redirects para indicarle a Django Redirecciona cualquier peticin de /music/ a /sections/arts/music/.. Esto es til cuando necesitas cambiar las cosas de lugar en tu sitio; los desarrolladores Web deberan hacer lo que est en sus manos para evitar los enlaces rotos.
198
Si no encuentra una coincidencia, el procesamiento de la peticin contina normalmente. El middleware slo se activa ante errores 404 no en errores 500 o respuestas con otros cdigos de estado. Notar que el orden de MIDDLEWARE_CLASSES es relevante. Generalmente puedes colocar RedirectFallbackMiddleware cerca del nal de la lista, debido a que se trata de una opcin de ltimo recurso. Nota: Si usas los middlewares redirect y atpages, analiza cual de los dos (redirect o atpages) desearas sea ejecutado primero. Sugerimos congurar atpages antes que redirects (o sea colocar el middleware atpages antes que el middleware redirects) pero tu podras decidir lo contrario.
199
Esto asegura que un simple POST a la URL example.com/logout no desconectar a un usuario; para que los usuarios puedan desconectarse, debern enviar una peticin a example.com/logout usando POST y enviar la variable POST confirm con el valor true. Bueno, aun con dichas medidas extra de seguridad, este esquema tambin puede ser atacado mediante CSRF la pgina maliciosa slo necesita hacer un poquito ms de trabajo. Los atacantes pueden crear un formulario completo que enve su peticin a tu sitio, ocultar el mismo en un <iframe> invisible y luego usar JavaScript para enviar dicho formulario en forma automtica.
200
necesita procesar la respuesta despus de SessionMiddleware, as que CsrfMiddleware debe aparecer antes que SessionMiddleware en la lista (esto es debido que el middleware de respuesta es procesado de atrs hacia adelante). Por otra parte, debe procesar la respuesta antes que la misma sea comprimida o alterada de alguna otra forma, de manera que CsrfMiddleware debe aparecer despus de GZipMiddleware. Una vez que has agregado eso a tu MIDDLEWARE_CLASSES ya ests listo. Revisa la seccin Orden de MIDDLEWARE_CLASSES_ en el Captulo 13 si necesitas conocer ms sobre el tema. En el caso en el que ests interesado, as es como trabaja CsrfMiddleware. Realiza estas dos cosas: 1. Modica las respuestas salientes a peticiones agregando un campo de formulario oculto a todos los formularios POST, con el nombre csrfmiddlewaretoken y un valor que es un hash del identicador de sesin ms una clave secreta. El middleware no modica la respuesta si no existe un identicador de sesin, de manera que el costo en rendimiento es despreciable para peticiones que no usan sesiones. 2. Para todas las peticiones POST que porten la cookie de sesin, comprueba que csrfmiddlewaretoken est presente y tenga un valor correcto. Si no cumple estas condiciones, el usuario recibir un error HTTP 403. El contenido de la pgina de error es el mensaje Cross Site Request Forgery detected. Request aborted. Esto asegura que solamente se puedan usar formularios que se hayan originado en tu sitio Web para enviar datos va POST al mismo. Este middleware deliberadamente trabaja solamente sobre peticiones HTTP POST (y sus correspondientes formularios POST). Como ya hemos explicado, las peticiones GET nunca deberan tener efectos colaterales; es tu responsabilidad asegurar eso. Las peticiones POST que no estn acompaadas de una cookie de sesin no son protegidas simplemente porque no tiene sentido protegerlas, un sitio Web malicioso podra de todas formas generar ese tipo de peticiones. Para evitar alterar peticiones no HTML, el middleware revisa la cabecera Content-Type de la respuesta antes de modicarla. Slo modica las pginas que son servidas como text/html o application/xml+xhtml. Limitaciones del middleware CSRF CsrfMiddleware necesita el framework de sesiones de Django para poder funcionar. (Revisa el Captulo 12 para obtener ms informacin sobre sesiones). Si ests usando un framework de sesiones o autenticacin personalizado que maneja en forma manual las cookies de sesin, este middleware no te ser de ayuda. Si tu aplicacin crea pginas HTML y formularios con algn mtodo inusual (por ej. si enva fragmentos de HTML en sentencias JavaScript document.write), podras estr salteandote el ltro que agrega el campo oculto al formulario. De presentarse esta situacin, el envo del formulario fallar siempre. (Esto sucede porque CsrfMiddleware usa una expresin regular para agregar el campo csrfmiddlewaretoken a tu HTML antes de que la pgina sea enviada al cliente, y la expresin regular a veces no puede manejar cdigo HTML muy extravagante). Si sospechas que esto podra estar sucediendo, slo examina el cdigo en tu navegador Web para ver si es que csrfmiddlewaretoken ha sido insertado en tu <form>. Para ms informacin y ejemplos sobre CSRF, visita http://en.wikipedia.org/wiki/CSRF.
201
15.6.1 apnumber
Para nmeros entre 1 y 9, este ltro retorna la representacin textual del nmero. Caso contrario retorna el numeral. Esto cumple con el estilo Associated Press. Ejemplos: 1 se convierte en uno. 2 se convierte en dos. 10 se convierte en 10. Puedes pasarle ya sea un entero o una representacin en cadena de un entero.
15.6.2 intcomma
Este ltro convierte un entero a una cadena conteniendo comas cada tres dgitos. Ejemplos: 4500 se convierte en 4,500. 45000 se convierte en 45,000. 450000 se convierte en 450,000. 4500000 se convierte en 4,500,000. Puedes pasarle ya sea un entero o una representacin en cadena de un entero.
15.6.3 intword
Este ltro convierte un entero grande a una representacin amigable en texto. Funciona mejor con nmeros mayores a un milln. Ejemplos: 1000000 se convierte en 1.0 milln. 1200000 se convierte en 1.2 milln. 1200000000 se convierte en 1.2 millardos. Se admiten valores hasta un billardo (1,000,000,000,000,000). Puedes pasarle ya sea un entero o una representacin en cadena de un entero.
15.6.4 ordinal
Este ltro convierte un entero a una cadena cuyo valor es su ordinal. Ejemplos: 1 se convierte en 1st. 2 se convierte en 2nd. 3 se convierte en 3rd. Puedes pasarle ya sea un entero o una representacin en cadena de un entero.
202
Para activar estos ltros, agrega django.contrib.markup a tu variable de conguracin INSTALLED_APPS. Una vez que hayas hecho esto, usa { % load markup %} en una plantilla y tendrs acceso a dichos ltros. Para ms detalles examina el cdigo fuente en django/contrib/markup/templatetags/markup.py.
15.8 Qu sigue?
Muchos de estos frameworks contribuidos (CSRF, el sistema de autenticacin, etc.) hacen su magia proveyendo una pieza de middleware. El middleware es esencialmente cdigo que se ejecuta antes y/o despus de cada peticin y puede modicar cada peticin y respuesta a voluntad. A continuacin trataremos el middleware incluido con Django y explicaremos cmo puedes crear el tuyo propio.
203
204
CAPTULO 16
En ocasiones, necesitars ejecutar una pieza de cdigo en todas las peticiones que maneja Django. ste cdigo puede necesitar modicar la peticin antes de que la vista se encargue de ella, puede necesitar registrar informacin sobre la peticin para propsitos de debugging, y as sucesivamente. Tu puedes hacer esto con el framework middleware de Django, que es un conjunto de acoples dentro del procesamiento de peticin/respuesta de Django. 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. Cada componente middleware es responsable de hacer alguna funcin especca. Si estas leyendo este libro de forma lineal (disculpen, posmodernistas), has visto middleware varias veces ya: Todas las herramientas de usuario y sesin que vimos en el Captulo 12 son posibles gracias a unas pequeas piezas de middleware (ms especcamente, el middleware hace que request.session y request.user estn disponibles para ti en las vistas. La cache global del sitio discutida en el Captulo 13 es solo una pieza de middleware que desva la llamada a tu funcin de vista si la respuesta para esa vista ya fue almacenada en la cache. Todas las aplicaciones contribuidas flatpages, redirects, y csrf del Captulo 14 hacen su magia a travs de componentes middleware. Este captulo se sumerge ms profundamente en qu es exactamente el middleware y cmo funciona, y explica cmo puedes escribir tu propio middleware.
16.1 Qu es middleware
Un componente middleware es simplemente una clase Python que se ajusta a una cierta API. Antes de entrar en los aspectos formales de los que es esa API, miremos un ejemplo muy sencillo. Sitios de trco alto a menudo necesitan implementar Django detrs de un proxy de balanceo de carga (mira el Captulo 20). Esto puede causar unas pequeas complicaciones, una de las cuales es que la IP remota de cada peticin (request.META[REMOTE_IP"]) ser la del balanceador de carga, no la IP real que realiza la peticin. Los balanceadores de carga manejan esto estableciendo una cabecera especial, X-Forwarded-For, con el valor real de la direccin IP que realiza la peticin. As que aqu est una pequea parte de middleware que le permite a los sitios que se ejecutan detrs de un proxy ver la direccin IP correcta en request.META[REMOTE_ADDR"]:
class SetRemoteAddrFromForwardedFor(object): def process_request(self, request): try: real_ip = request.META[ HTTP_X_FORWARDED_FOR ] except KeyError:
205
pass else: # HTTP_X_FORWARDED_FOR can be a comma-separated list of IPs. # Take just the first one. real_ip = real_ip.split( " , " )[0] request.META[ REMOTE_ADDR ] = real_ip
Si esto es instalado (mira la siguiente seccin), el valor de X-Forwarded-For de todas las peticiones ser automticamente insertado en request.META[REMOTE_ADDR]. Esto signica que tus aplicaciones Django no necesitan conocer si estn detrs de un proxy de balanceo de carga o no, pueden simplemente acceder a request.META[REMOTE_ADDR], y eso funcionar si se usa un proxy o no. De hecho, es una necesidad tan comn, que esta pieza de middleware ya viene incorporada en Django. Esta ubicada en django.middleware.http, y puedes leer ms sobre ella en la siguiente seccin.
Una instalacin Django no requiere ningn middleware La tupla MIDDLEWARE_CLASSES puede estar vaca, si tu quieres pero te recomendamos que actives CommonMiddleware, la cual explicaremos en breve. El orden es importante. En las fases de peticin y vista, Django aplica el middleware en el orden que gura en MIDDLEWARE_CLASSES, y en las fases de respuesta y excepcin, Django aplica el middleware en el orden inverso. Es decir, Django trata MIDDLEWARE_CLASSES como una especie de wrapper alrededor de la funcin de vista: en la peticin recorre hacia abajo la lista hasta la vista, y en la respuesta la recorre hacia arriba. Mira la seccin Cmo procesa una peticin Django: Detalles completos_ en el Captulo3 para un repaso de las fases.
206
Por razones de rendimiento, cada clase middleware activada es instanciada slo una vez por proceso servidor. Esto signica que __init__() es llamada slo una vez al iniciar el servidor no para peticiones individuales. Una razn comn para implementar un mtodo __init__() es para vericar si el middleware es en realidad necesario. Si __init__() emite django.core.exceptions.MiddlewareNotUsed, entonces Django remover el middleware de la pila de middleware. Tu podras usar esta caracterstica para vericar si existe una pieza de software que la clase middleware requiere, o vericar si el servidor esta ejecutndose en modo debug, o cualquier otra situacin similar. Si una clase middleware dene un mtodo __init__(), ste no debe tomar argumentos ms all del estndar self.
Los parmetros deben ser bastante auto-explicativos: request es el objeto peticin, y response es el objeto respuesta retornados por la vista. A diferencia de los pre-procesadores de peticin y vista, los cuales pueden retornar None, process_response() debe retornar un objeto HttpResponse. Esa respuesta puede ser la respuesta original pasada a la funcin (posiblemente modicada) o una totalmente nueva.
208
Nota el import re, ya que DISALLOWED_USER_AGENTS requiere que sus valores sean expresiones regulares compiladas (es decir, el resultado de re.compile()). El archivo de conguracin es un archivo comn de Python, por lo tanto es perfectamente adecuado incluir sentencias import en l. *Realiza re-escritura de URL basado en las conguraciones APPEND_SLASH y PREPEND_WWW*: Si APPEND_SLASH es igual a True, las URLs que no poseen una barra al nal sern redirigidas a la misma URL con una barra al nal, a menos que el ltimo componente en el path contenga un punto. De esta manera foo.com/bar es redirigido a foo.com/bar/, pero foo.com/bar/file.txt es pasado a travs sin cambios. Si PREPEND_WWW es igual a True, las URLs que no poseen el prejo www. sern redirigidas a la misma URL con el prejo www.. Ambas opciones tienen por objeto normalizar URLs. La losofa es que cada URL debera existir en un y slo un lugar. Tcnicamente la URL example.com/bar es distinta de example.com/bar/, la cual a su vez es distinta de www.example.com/bar/. Un motor de bsqueda indexador tratara de forma separada estas URLs, lo cual es perjudicial para la valoracin de tu sitio en el motor de bsqueda, por lo tanto es una buena prctica normalizar las URLs. Maneja ETags basado en la conguracin USE_ETAGS: ETags son una optimizacin a nivel HTTP para almacenar condicionalmente las pginas en la cach. Si USE_ETAGS es igual a True, Django calcular una ETag para cada peticin mediante la generacin de un hash MD5 del contenido de la pgina, y se har cargo de enviar respuestas Not Modified, si es apropiado. Nota tambin que existe un middleware de GET condicional, que veremos en breve, el cual maneja ETags y hace algo ms.
209
210
16.5 Qu sigue?
Los desarrolladores Web y los diseadores de esquemas de bases de datos no siempre tienen el lujo de comenzar desde cero. En el prximo captulo, vamos a cubrir el modo de integrarse con sistemas existentes, tales como esquemas de bases de datos que has heredado de la dcada de los 80.
16.5. Qu sigue?
211
212
CAPTULO 17
Django es el ms adecuado para el desarrollo denominado de campo verde es decir, comenzar proyectos desde cero, como si estuviramos construyendo un edicio en un campo de verde pasto fresco. Pero a pesar de que Django favorece a los proyectos iniciados desde cero, es posible integrar el framework con bases de datos y aplicaciones existentes 1 . Este captulo explica algunas de las estrategias de integracin.
213
3. Crea una aplicacin dentro de tu proyecto ejecutando python mysite/manage.py startapp myapp (donde myapp es el nombre de tu aplicacin). Usaremos myapp como el nombre de aplicacin aqu. 4. Ejecuta el comando python mysite/manage.py inspectdb. Esto examinar las tablas en la base de datos DATABASE_NAME e imprimir para cada tabla el modelo de clase generado. Hecha una mirada a la salida para tener una idea de lo que puede hacer inspectdb. 5. Guarda la salida en el archivo models.py dentro de tu aplicacin usando la redireccin de salida estndar de la shell: python mysite/manage.py inspectdb > mysite/myapp/models.py 6. Edita el archivo mysite/myapp/models.py para limpiar los modelos generados y realiza cualquier personalizacin necesaria. Te daremos algunas sugerencias para esto en la siguiente seccin.
No solo estas lneas son redundantes, sino que pueden causar problemas si tu aplicacin agregara nuevos registros a estas tablas. El comando inspectdb no puede detectar si un campo es autoincrementado, as que est en t cambiar esto a AutoField, si es necesario. 3. Cada tipo de campo (ej., CharField, DateField) es determinado mirando el tipo de la columna de la base de datos (ej., VARCHAR, DATE). Si inspectdb no puede mapear un tipo de columna a un tipo de campo del modelo, usar TextField e insertar el comentario Python This field type is a guess. a continuacin del campo en el modelo generado. Mantn un ojo en eso, y cambia el tipo de campo adecuadamente si es necesario. Si un campo en tu base de datos no tiene un buen equivalente en Django, con seguridad puedes dejarlo fuera. La capa de modelo de Django no requiere que incluyas todos los campos de tu(s) tabla(s). 4. Si un nombre de columna de tu base de datos es una palabra reservada de Python (como pass, class o for), inspectdb agregar _field al nombre del atributo y establecer el atributo db_column al nombre real del campo (ej., pass, class, o for). Por ejemplo, si una tabla tiene una columna INT llamada for, el modelo generado tendr un campo como este:
for_field = models.IntegerField(db_column= for )
inspectdb insertar el comentario Python Field renamed because it was a Python reserved word. a continuacin del campo. 5. Si tu base de datos contiene tablas que hacen referencia a otras tablas (como la mayora de las bases de datos lo hacen), tal vez tengas que re-acomodar el orden de los modelos generados, de manera que los modelos que hacen referencia a otros modelos estn ordenados apropiadamente. 214 Captulo 17. Captulo 16: Integracin con Base de datos y Aplicaciones existentes
Por ejemplo, si un modelo Book tiene una ForeignKey al modelo Author, el modelo Author debe ser denido antes del modelo Book. Si necesitas crear una relacin en un modelo que todava no esta denido, puedes usar el nombre del modelo, en vez del objeto modelo en s. 6. inspectdb detecta claves primarias para PostgreSQL, MySQL y SQLite. Es decir, inserta primary_key=True donde sea necesario. Para otras bases de datos, necesitars insertar primary_key=True para al menos un campo en cada modelo, ya que los modelos Django requieren tener un campo primary_key=True. 7. La deteccin de claves forneas slo funciona con PostgreSQL y con ciertos tipos de tablas MySQL. En otros casos, los campos de clave fornea sern generados como campos IntegerField, asumiendo que la columna de clave fornea fue una columna INT.
Ese es el esquema bsico de autenticacin que verica la base de datos de usuarios de Django. El orden de AUTHENTICATION_BACKENDS se tiene en cuenta, por lo que si el mismo usuario y contrasea son vlidos en mltiples back-ends, Django detendr el procesamiento en la primera coincidencia positiva.
215
class MyBackend(object): def authenticate(self, username=None, password=None): # Check the username/password and return a User.
De cualquier manera, authenticate debe vericar las credenciales que recibe, y debe retornar un objeto User que coincide con esas credenciales, si las credenciales son vlidas. Si no son vlidas, debe retornar None. El sistema de administracin de Django esta altamente acoplado a su propio objeto User respaldado por base de datos descripto en el Captulo 12. La mejor manera de lidiar con esto es crear un objeto User de Django para cada usuario que existe en tu back-end (ej., en tu directorio LDAP, tu base de datos SQL externa, etc.). De cualquier manera puedes escribir un script para hacer esto por adelantado o tu mtodo de autenticacin puede hacerlo la primera vez que el usuario ingresa al sistema. Aqu est un ejemplo de back-end que autentica contra unas variables de usuario y contrasea denidas en tu archivo settings.py y crea un objeto User de Django la primera vez que un usuario se autentica:
from django.conf import settings from django.contrib.auth.models import User, check_password class SettingsBackend(object): """ Authenticate against the settings ADMIN_LOGIN and ADMIN_PASSWORD. Use the login name, and a hash of the password. For example: ADMIN_LOGIN = admin ADMIN_PASSWORD = sha1$4e987$afbcf42e21bd417fb71db8c66b321e9fc33051de """ def authenticate(self, username=None, password=None): login_valid = (settings.ADMIN_LOGIN == username) pwd_valid = check_password(password, settings.ADMIN_PASSWORD) if login_valid and pwd_valid: try: user = User.objects.get(username=username) except User.DoesNotExist: # Create a new user. Note that we can set password # to anything, because it wont be checked; the password # from settings.py will. user = User(username=username, password= get from settings.py ) user.is_staff = True user.is_superuser = True user.save() return user return None def get_user(self, user_id): try: return User.objects.get(pk=user_id) except User.DoesNotExist: return None
216
Captulo 17. Captulo 16: Integracin con Base de datos y Aplicaciones existentes
Aqu, la lnea <Location /"> signica maneja cada URL, comenzando en la raz, con Django. Esta perfectamente bien limitar esta directiva <Location> a cierto rbol de directorio. Por ejemplo, digamos que tienes una aplicacin PHP existente que impulsa la mayora de las pginas en un dominio y quieres instalar el sitio de administracin de Django en /admin/ sin afectar el cdigo PHP. Para hacer esto, slo congura la directiva <Location> a /admin/:
<Location "/admin/"> SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.settings PythonDebug On </Location>
Con esto en su lugar, slo las URLs que comiencen con /admin/ activarn Django. Cualquier otra pgina usar cualquier infraestructura que ya exista. Nota que adjuntar Django a una URL calicada (como /admin/ en el ejemplo de esta seccin) no afecta a Django en el anlisis de las URLs. Django trabaja con la URL absoluta (ej., /admin/people/person/add/), no con una versin recortada de la URL (ej., /people/person/add/). Esto signica que tu URLconf raz debe incluir el prejo /admin/.
17.4 Qu sigue?
Hablando del sitio de administracin de Django y sobre cmo acomodar el framework para encajar con necesidades existentes, otra tarea comn es personalizar el sitio de administracin de Django. El prximo captulo se enfoca en dicha personalizacin.
217
218
Captulo 17. Captulo 16: Integracin con Base de datos y Aplicaciones existentes
CAPTULO 18
El Captulo 6 introdujo la interfaz de administracin de Django, y ya es tiempo de volver atrs y dar una mirada ms minuciosa al asunto. Como dijimos varias veces antes, la interfaz de administracin es una de las caractersticas ms sobresalientes de este framework, y la mayora de los desarrolladores que usan Django lo encuentran til y eciente. Debido a que esta interfaz es tan popular, es comun que los desarrolladores quieran personalizarlo o extenderlo. Las ultimas secciones del Captulo 6 ofrecieron algunas maneras simples de personalizar ciertos aspectos de la interfaz. Antes de continuar con este captulo, considera revisar ese material; cubre como personalizar las listas de cambio y los formularios de edicin, asi como una forma fcil de remarcar la interfaz para que se indentique con tu sitio. El capitulo 6 discute tambin cuando y porqu querras usar la interfaz de administracin y desde hicimos un gran salto desde esos prrafos hasta este punto, lo reproduciremos nuevamente aqu: Obviamente, es muy til para modicar datos (se vea venir). Si tenemos cualquier tipo de tarea de introduccin de datos, el administrador es lo mejor que hay. Sospechamos que la gran mayora de lectores de este libro tiene una horda de tareas de este tipo. La interfaz de administracin de Django brilla especialmente cuando usuarios no tcnicos necesitan ser capaces de ingresar datos; ese es el propsito detrs de esta caracteristica, despus de todo. En el peridico donde Django fue creado orginalmente, el desarrollo de una caracterstica tipica online un reporte especial sobre la calidad del agua del acueducto municipal, pongamos implicaba algo as: El periodista responsable del artculo se reune con uno de los desarrolladores y discuten sobre la informacin disponible. El desarrollador disea un modelo basado en esta informacin y luego abre la interfaz de administracin para el periodista. Mientras el periodista ingresa datos a Django, el programador puede enforcarse en desarrollar la interfaz accesible publicamente (la parte divertida!). En otras palabras, la razn de ser de la interfaz de administracin de Django es facilitar el trabajo simultneo de productores de contenido y programadores. Sin embargo, ms all de estas tareas de entrada de datos obvias, encontramos que la interfaz de administracin 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 administracin e ingresar algunos datos de relleno. Esto es usual
219
para encontrar errores de modelado; tener una una interfaz grca al modelo revela problemas rpidamente. Gestin de datos adquiridos: Hay una pequea entrada de datos asociada a un sitio como http://chicagocrime.org, puesto que la mayora de los datos provienen de una fuente automtica. No obstante, cuando surgen problemas con los datos automticos, es til poder entrar y editarlos fcilmente. La interfaz de administracin de Django maneja estos casos comunes con algunas o ninguna personalizacin. Aunque, como sucede con la mayora de las generalizaciones en el diseo, la gestin unicada de todos estos casos signica que la interfaz no maneja igual de bien otros modos de edicin. Hablaremos de estos casos para los que la interfaz de administracin de Django no est diseada un poquito ms adelante, pero primero, vayamos a una breve disgrecin para una discusin losca.
220
221
La mayora de las veces, sin embargo, querrs cambiar la plantilla slo para un nico objeto o aplicacin (no globalmente). As, cada vista busca primero plantillas para modelos y aplicaciones especcas, en el siguiente orden: admin/<app_label>/<object_name>/<template>.html admin/<app_label>/<template>.html admin/<template>.html Por ejemplo, la vista del formulario de agregar/editar para un modelo Libro en la aplicacin libros busca plantillas en este orden: admin/books/book/change_form.html admin/books/change_form.html admin/change_form.html
Figura 17-1. Un formulario de edicin de libros personalizado Esta es una manera muy fcil de hacerlo: simplemente crea admin/libreria/libro/change_form.html e inserta este cdigo: una plantilla llamada
222
{ % extends "admin/change_form.html" %} { % block form_top %} <p>Insert meaningful help message here...</p> { % endblock %}
Todas estas plantillas denen un nmero de bloques que puedes sobreescribir. Como con la mayora de los programas, la mejor documentacin es el propio cdigo, por lo que te animamos a mirar las plantillas originales (que se encuentran en django/contrib/admin/templates/) para trabajar con la informacin ms actualizada.
{% block extrahead %} <script src= "http://media.ejemplo.com/javascript/jquery.js" type= "text/javascript" ></script> <script type= "text/javascript" > // code to actually use jQuery here... </script> {% endblock %}
Nota No estamos seguros porqu necesitaras jQuery en la pgina de historia de objetos, pero, por supuesto, este ejemplo es vlido para cualquier plantilla de la interfaz de administracin. Puedes usar esta tcnica para incluir cualquier tipo de controladores JavaScript que puedas necesitar en tus formularios.
223
A modo de ejemplo, agreguemos una vista reporte de editores a nuestra aplicacin de libros del Captulo 6. Construiremos una vista de administracin que muestre la lista de libros en funcin de los editores un ejemplo bastante tpico de vista de reporte que puedes necesitar construir. Primero, actualicemos nuestro archivo URLconf. Necesitamos insertar esta lnea:
( r ^admin/books/report/$ , mysite.books.admin_views.report ),
antes de la lnea que incluye las vistas del administrador. Un esqueleto del URLconf puede parecerse a algo as:
from django.conf.urls.defaults import * urlpatterns = patterns( , ( r ^admin/libreria/reporte/$ , libreria.admin_views.reporte ), ( r ^admin/ , include( django.contrib.admin.urls )), )
Por qu ponemos la vista personalizada antes de incluir las del administrador? Recuerda que Django procesa los patrones de URL en orden. La inclusin de los patrones de urls del administrador coincide con casi cualquier cosa que llega a su punto de inclusin, por lo que si invertimos el orden de esas lineas, Django encontrar una vista por omisin para ese patrn y no funcionar como queremos. En este caso particular, intentar cargar un un lista de cambios para un modelo Reporte en la aplicacin libros, que no existe. Ahora escribamos nuestra vista. Para hacer honor a la simplicidad, slo cargaremos todos los libros dentro del contexto, y dejaremos que la plantilla maneje el agrupamiento con la etiqueta { % regroup %}. Crea un archivo books/admin_views.py, con este cdigo:
from from from from mysite.books.models import Book django.template import RequestContext django.shortcuts import render_to_response django.contrib.admin.views.decorators import staff_member_required
def report(request): return render_to_response( " admin/books/report.html " , { book_list : Book.objects.all()}, RequestContext(request, {}), ) report = staff_member_required(report)
Debido a que dejamos el agrupamiento a la plantilla, esta vista es bastante simple. Sin embargo, hay algunos fragmentos sutiles dignos de explicitar: Usamos el decorador staff_member_required de django.contrib.admin.views.decorators. Esto es similar a login_required discutido en el Captulo 12, pero este decorador tambin verica que el usuario est marcado como un mientro del staff, y tenga en consecuencia acceso a la interfaz de administracin Este decorador protege todos las vistas predenidas del administrador, y hace que la lgica de autenticacin para tus vistas coincida con la del resto de la interfaz. Renderizamos una plantilla que se encuntra bajo admin/. Aunque esto no es estrictamente requerido, se considera una buena practica para mantener todas tus plantillas de administracin agrupadas en un directorio admin/. Tambin pusimos la plantilla en un directorio llamado books luego de nuestra aplicacin lo que es tambin una buena prctica. Usamos RequestContext como el tercer parmetro (context_instance) para render_to_response. Esto asegura que la informacin sobre el usuario en curso est disponible para la plantilla. Mira el Captulo 10 para saber ms sobre RequestContext. 224 Captulo 18. Captulo 17: Extender la Interfaz de Administracin de Django
Finalmente, haremos una plantilla para esta vista. Extenderemos una plantilla de la administracin para que lograr que nuestra vista coincida visualmente con el resto de la interfaz:
{% extends "admin/base_site.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.name" by publisher as books_by_publisher %} {% for publisher in books_by_publisher %} <h3>{{ publisher.grouper }}</h3> <ul> {% for book in publisher.list|dictsort:"title" %} <li>{{ book }}</li> {% endfor %} </ul> {% endfor %} </div> {% endblock %}
Al extender admin/base_site.html, conseguimos el look and feel de la interfaz de administracin de Django gratis. La Figura 17-2 muestra como luce el resultado.
Figura 17-2. Una vista personalizada libros por editor. Puedes usar esta tcnica para agregar cualquier cosa que suees para la interfaz de administracin. Recuerda que las llamadas vistas de administracin personalizadas en realidad son slo vistas comunes de Django; por lo que puedes usar todas las tcnicas aprendidas en el resto de este libro para proveer una interfaz con tanta complejidad como necesites. Cerraremos este captulo con algunas ideas para vistas de administracin personalizadas. 18.3. Crear vistas de administracin personalizadas 225
Si esta linea aparece antes que las URLs de administracion en tu URLconf, la vista agregar_por_isbn reemplazar completamente a la vista estndar para ese modelo. Podriamos seguir un truco similar para reemplazar la pgina de conrmacin de eliminacin, la de edicin o cualquier otra parte de la interfaz.
18.5 Qu sigue?
Si tu idioma nativo es el ingls cosa que gracias a los traductores ya no es necesaria para leer este libro quizas no te hayas enterado de una las ms fantsticas caractersticas de la interfaz de administracion: est disponible en casi 40 idiomas distintos! Esto es posible gracias al framework de internacionalizacin de Django (y el duro trabajo de los traductores voluntarios de Django). El prximo captulo explica como usar este framework para crear sitios Django localizados. Avanti!
226
CAPTULO 19
Django fue originalmente desarrollado exactamente en el medio de los Estados Unidos (literalmente; Lawrence, Kansas, se halla a menos de 40 millas del centro geogrco de la porcin continental de los Estados Unidos). Como la mayora de los proyectos open source, sin embargo, la comunidad de Django creci hasta incluir gente de todo el globo. A medida que la comunidad fue tornndose ms diversa, la internacionalizacin y la localizacin fueron tomando una importancia creciente. Debido a que muchos desarrolladores tienen, en el mejor de los casos, una comprensin difusa de dichos trminos vamos a denirlos brevemente. Internacionalizacin se reere al proceso de diseo 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 traduccin, la abstraccin de la visualizacin de fechas y horarios de manera que sea posible respetar diferentes estndares locales, la provisin de lo necesario para admitir diferentes zonas horarias, y en general el asegurarse de que el cdigo no contenga ninguna suposicin acerca de la ubicacin de sus usuarios. Encontrars a menudo internacionalizacin abreviada como I18N (el nmero 18 se reere al nmero de letras omitidos entre la I inicial y la N nal). Localizacin se reere al proceso especco de traducir un programa internacionalizado para su uso en un *locale* particular. Encontrars a menudo localizacin abreviada como L10N. Django en si est totalmente internacionalizado; todas las cadenas estn marcadas para su traduccin, y existen variables de conguracin que controlan la visualizacin de valores dependientes del *locale* como fechas y horarios. Django tambin incluye ms de 40 archivos de localizacin. Si no hablas ingls en forma nativa, existe una buena probabilidad de que Django ya se encuentre traducido a tu idioma nativo. El mismo framework de internacionalizacin usado para esas localizaciones est disponible para que lo uses en tu propio cdigo y plantillas. En resumen, necesitars agregar una cantidad mnima de *hooks* a tu cdigo Python y a tus plantillas. Esos *hooks* reciben el nombre de cadenas de traduccin. Los mismos le indican a Django Este texto debe ser traducido al idioma del usuario nal si existe una traduccin a dicho idioma de ese texto. Django se encarga de usar esos *hooks* para traducir las aplicaciones Web al vuelo de acuerdo a las preferencias de idioma del usuario. Esencialmente, Django hace dos cosas: Le permite a los desarrolladores y autores de plantillas especicar qu partes de sus aplicaciones deben ser traducibles. Usa esta informacin para traducir las aplicaciones Web para usuarios particulares de acuerdo a sus preferencias de idioma. La maquinaria de traduccin de Django usa gettext de GNU (http://www.gnu.org/software/gettext/) via el mdulo estndar gettext incluido en Python.
227
Si no necesitas usar internacionalizacin: Los *hooks* de internacionalizacin de Django se encuentran activos por omisin, lo cual incurre en un pequeo *overhead*. Si no utilizas internacionalizacin, deberas establecer USE_I18N = False en tu archivo de conguracin. Si USE_I18N tiene el valor False Django implementar algunas optimizaciones de manera de no cargar la maquinaria de localizacin. Probablemente querrs tambin eliminar django.core.context_processors.i18n de tu variable de conguracin TEMPLATE_CONTEXT_PROCESSORS.
La mayora de los desarrolladores preere usar _(), debido a que es ms corta. La traduccin funciona tambin sobre valores computados. Este ejemplo es idntico a los dos anteriores:
def my_view(request): words = [ Welcome , to , my , site. ] output = _( .join(words)) return HttpResponse(output)
La traduccin funciona tambin sobre variables. De nuevo, este es otro ejemplo idntico:
def my_view(request): sentence = Welcome to my site. output = _(sentence) return HttpResponse(output)
(algo a tener en cuenta cuando se usan variables o valores computados, como se vea en los dos ejemplos previos, es que la utilidad de deteccin de cadenas de traduccin de Django, make-messages.py, no ser capaz de encontrar esas cadenas. Trataremos make-messages ms adelante). Las cadenas que le pasas a _() o gettext() pueden contener marcadores de posicin (por placeholders), especicados con la sintaxis estndar de interpolacin de cadenas con nombres, por ejemplo:
228
Esta tcnica permite que las traducciones especcas de cada idioma reordenen el texto de los marcadores de posicin. Por ejemplo, una traduccin al ingls podra ser Adrian is my name, mientras que una traduccin al espaol podra ser Me llamo Adrian, con el marcador de posicin (el nombre) ubicado a continuacin del texto traducido y no antes del mismo. Por esta razn, deberas usar interpolacin de cadenas con nombres (por ejemplo %(name)s) en lugar de interpolacin posicional (por ejemplo %s o %d). Si usas interpolacin posicional las traducciones no sern capaces de reordenar el texto de los marcadores de posicin.
En este ejemplo, gettext_lazy() almacena una referencia perezosa a la cadena no el verdadero texto traducido. La traduccin en si misma se llevar a cabo cuando sea usada en un contexto de cadena, tal como el renderizado de una plantilla en el sitio de administracin de Django. Si no te gusta el nombre largo gettext_lazy puedes simplemente crear un alias _ (guin bajo) para el mismo, de la siguiente forma:
from django.utils.translation import gettext_lazy as _ class MyThing(models.Model): name = models.CharField(help_text=_( This is the help text ))
Usa siempre traducciones perezosas en modelos Django (de lo contrario no sern traducidos correctamente para cada usuario). Y es una buena idea agregar tambin traducciones de los nombres de campos y nombres de tablas. Esto signica escribir las opciones verbose_name y verbose_name_plural en forma explcita en la clase Meta:
from django.utils.translation import gettext_lazy as _ class MyThing(models.Model): name = models.CharField(_( name ), help_text=_( This is the help text )) class Meta: verbose_name = _( my thing ) verbose_name_plural = _( mythings )
229
19.1.4 Pluralizacin
Usa la funcin django.utils.translation.ngettext() para especicar mensajes que tienen formas singular y plural distintas, por ejemplo:
from django.utils.translation import ngettext def hello_world(request, count): page = ngettext( there is %(count)d object , there are %(count)d ) % { count : count} return HttpResponse(page) objects , count
ngettext tiene tres argumentos: la cadena de traduccin singular, la cadena de traduccin plural y el nmero de objetos (el cual es pasado a los idiomas de traduccin como la variable count).
Si solo deseas marcar un valor para traduccin pero para traduccin posterior, usa la opcin noop:
<title>{ % trans "value" noop %}</title>
No es posible usar variables de plantilla en { % trans %} solo estn permitidas cadenas constantes, rodeadas de comillas simples o dobles. Si tu traduccin requiere variables (marcadores de posicin) puedes usar por ejemplo { % blocktrans %}:
{ % blocktrans %}This will have {{ value }} inside.{ % endblocktrans %}
Para traducir una expresin de plantilla por ejemplo, cuando usas ltros de plantillas necesitas asociar la expresin a una variable local que ser la que se usar dentro del bloque de traduccin:
{ % blocktrans with value|filter as myvar %} This will have {{ myvar }} inside. { % endblocktrans %}
Si necesitas asociar ms de una expresin dentro de una etiqueta blocktrans, 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, especica tanto la forma singular como la plural con la etiqueta { % plural %} la cual aparece dentro de { % blocktrans %} y { % endblocktrans %}, por ejemplo:
{ % blocktrans count list|length as counter %} There is only one {{ name }} object. { % plural %} There are {{ counter }} {{ name }} objects. { % endblocktrans %}
Internamente, todas las traducciones en bloque y en lnea usan las llamadas apropiadas a gettext/ngettext.
230
Cuando usas RequestContext (ver Captulo 10), tus plantillas tienen acceso a tres variables especcas relacionadas con la traduccin: {{ LANGUAGES }} es una lista de tuplas en las cuales el primer elemento es el cdigo de idioma y el segundo es el nombre y escrito usando el mismo). {{ LANGUAGE_CODE }} es el idioma preferido del usuario actual, expresado como una cadena (por ejemplo en-us). (Consulta la seccin Cmo descubre Django la preferencia de idioma para informacin adicional). {{ LANGUAGE_BIDI }} es el sistema de escritura del idioma actual. Si el valor es True, se trata de un idioma derecha-a-izquierda (por ejemplo hebreo, rabe). Si el valor es False, se trata de de un idioma izquierda-a-derecha (por ejemplo ingls, francs, alemn). Puedes tambin 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 %}
Tambin existen *hooks* de traduccin que estn disponibles en el interior de cualquier etiqueta de bloque de plantilla que acepte cadenas constantes. En dichos casos basta con que uses la sintaxis _() para especicar una cadena de traduccin, por ejemplo:
{ % some_special_tag _("Page not found") value|yesno:_("yes,no") %}
En este caso tanto la etiqueta como el ltro vern la cadena ya traducida (en otras palabras la cadena es traducida antes de ser pasada a las funciones de manejo de etiquetas), de manera que no necesitan estar preparadas para manejar traduccin.
donde de es el cdigo de idioma para el archivo de mensajes que deseas crear. El cdigo de idioma en este caso est en formato locale. Por ejemplo, el mismo es pt_BR para portugus de Brasil y de_AT para alemn de Austria. Echa un vistazo a los cdigos de idioma en el directorio django/conf/locale/ para ver cuales son los idiomas actualmente incluidos. El script debe ser ejecutado desde una de tres ubicaciones: El directorio raz django (no una copia de trabajo de Subversion, sino el que se halla referenciado por $PYTHONPATH o que se encuentra en algn punto debajo de esa ruta. El directorio raz de tu proyecto Django
231
El directorio raz de tu aplicacin Django El script recorre completamente el rbol en el cual es ejecutado y extrae todas las cadenas marcadas para traduccin. Crea (o actualiza) un archivo de mensajes en el directorio conf/locale. En el ejemplo de, el archivo ser conf/locale/de/LC_MESSAGES/django.po. Si es ejecutado sobre el rbol de tu proyecto o tu aplicacin, har lo mismo pero la ubicacin del directorio locale es locale/LANG/LC_MESSAGES (nota que no tiene un prejo conf). La primera vez que lo ejecutes en tu rbol necesitars crear el directorio locale. Sin gettext? Si no tienes instaladas las utilidades gettext, make-messages.py crear archivos vacos. Si te encuentras ante esa situacin debes o instalar dichas utilidades o simplemente copiar el archivo de mensajes de ingls (conf/locale/en/LC_MESSAGES/django.po) y usar el mismo como un punto de partida; se trata simplemente de un archivo de traduccin vaco. El formato de los archivos .po es sencillo. Cada archivo .po contiene una pequea cantidad de metadatos tales como la informacin de contacto de quines mantienen la traduccin, pero el grueso del archivo es una lista de mensajes mapeos simples entre las cadenas de traduccin y las traducciones al idioma en cuestin propiamente dichas. Por ejemplo, si tu aplicacin Django contiene una cadena de traduccin para el texto Welcome to my site:
_( " Welcome to my site. " )
entonces make-messages.py habr creado un archivo .po que contendr el siguiente fragmento un mensaje:
#: path/to/python/module.py:23 msgid "Welcome to my site." msgstr ""
Es necesaria una rpida explicacin: msgid es la cadena de traduccin, la cual aparece en el cdigo fuente. No la modiques. msgstr es donde colocas la traduccin especca a un idioma. Su valor inicial es vaco de manera que es tu responsabilidad el cambiar esto. Asegrate de que mantienes las comillas alrededor de tu traduccin. Por conveniencia, cada mensaje incluye el nombre del archivo y el nmero de lnea desde el cual la cadena de traduccin fue extrada. Los mensajes largos son un caso especial. La primera cadena inmediatamente a continuacin de msgstr (o msgid) es una cadena vaca. El contenido en si mismo se encontrar en las prximas lneas con el formato de una cadena por lnea. Dichas cadenas se concatenan en forma directa. No olvides los espacios al nal de las cadenas; en caso contrario todas sern agrupadas sin espacios entre las mismas!. Por ejemplo, a continuacin vemos una traduccin de mltiples lneas (extrada de la localizacin al espaol incluida con Django):
msgid "" "Theres been an error. Its been reported to the site administrators via e-" "mail and should be fixed shortly. Thanks for your patience." msgstr "" "Ha ocurrido un error. Se ha informado a los administradores del sitio " "mediante correo electrnico y debera arreglarse en breve. Gracias por su " "paciencia."
Notar los espacios nales. Ten en cuenta el conjunto de caracteres 232 Captulo 19. Captulo 18: Internacionalizacin
Cuando crees un archivo .po con tu editor de texto favorito, primero edita la lnea del conjunto de caracteres (busca por el texto HARSET") y ja su valor al del conjunto de caracteres usars para editar el contenido. Generalmente, UTF-8 debera funcionar para la mayora de los idiomas pero gettext debera poder manejar cualquier conjunto de caracteres. Para reexaminar todo el cdigo fuente y las plantillas en bsqueda de nuevas cadenas de traduccin y actualizar todos los archivos de mensajes para todos los idiomas, ejecuta lo siguiente:
make-messages.py -a
233
django.middleware.locale.LocaleMiddleware )
LocaleMiddleware intenta determinar la preferencia de idioma del usuario siguiendo el siguiente algoritmo: Primero, busca una clave django_language en la sesin del usuario actual. Se eso falla, busca una cookie llamada django_language. Si eso falla, busca la cabecera HTTP Accept-Language. Esta cabecera es enviada por tu navegador y le indica al servidor qu idioma(s) preeres en orden de prioridad. Django intenta con cada idioma que aparezca en dicha cabecera hasta que encuentra uno para el que haya disponible una traduccin. Si eso falla, usa la variable de conguracin global LANGUAGE_CODE. En cada uno de dichas ubicaciones, el formato esperado para la preferencia de idioma es el formato estndar, como una cadena. Por ejemplo, portugus de Brasil es pt-br. Si un idioma base est disponible pero el sub-idioma especicado no, Django usar el idioma base. Por ejemplo, si un usuario especica de-at (alemn Austraco) pero Django solo tiene disponible de , usar de. Slo pueden seleccionarse idiomas que se encuentren listados en la variable de conguracin LANGUAGES. Si deseas restringir la seleccin de idiomas a un subconjunto de los idiomas provistos (debido a que tu aplicacin no incluye todos esos idiomas), ja tu LANGUAGES a una lista de idiomas, por ejemplo:
LANGUAGES = ( ( de , _( German )), ( en , _( English )), )
Este ejemplo restringe los idiomas que se encuentran disponibles para su seleccin automtica a alemn e ingls (y cualquier sub-idioma, como de-ch o en-us). Si denes un LANGUAGES personalizado es posible marcar los idiomas como cadenas de traduccin pero usa una funcin gettext() boba, no la que se encuentra en django.utils.translation. Nunca debes importar django.utils.translation desde el archivo de conguracin debido a que ese mdulo a su vez depende de las variables de conguracin, y eso creara una importacin circular. La solucin es usar una funcin gettext() boba. A continuacin un archivo de conguracin de ejemplo:
_ = lambda s: s LANGUAGES = ( ( de , _( German )), ( en , _( English )), )
Con este esquema, make-messages.py todava ser capaz de encontrar y marcar dichas cadenas para su traduccin pero la misma no ocurrir en tiempo de ejecucin, de manera que tendrs que recordar envolver los idiomas con la verdadera gettext() en cualquier cdigo que use LANGUAGES en tiempo de ejecucin. El LocaleMiddleware slo puede seleccionar idiomas para los cuales exista una traduccin base provista por Django. Si deseas ofrecer traducciones para tu aplicacin que no se encuentran en el conjunto de traducciones incluidas en el cdigo fuente de Django, querrs proveer al menos traducciones bsicas para ese idioma. Por ejemplo, Django usa identicadores de mensajes tcnicos para traducir formatos de fechas y de horas as que necesitars al menos esas traducciones para que el sistema funcione correctamente. Un buen punto de partida es copiar el archivo .po de ingls y traducir al menos los mensajes tcnicos, y quiz tambin los mensajes de los validadores.
234
Los identicadores de mensajes tcnicos son fcilmente reconocibles; estn completamente en maysculas. No necesitas traducir los identicadores de mensajes como lo haces con otros mensajes; en cambio, deber proporcionar la variante local correcta del valor provisto en ingls. Por ejemplo, con DATETIME_FORMAT (o DATE_FORMAT o TIME_FORMAT), este sera la cadena de formato que deseas usar en tu idioma. El formato es idntico al de la cadena de formato usado por la etiqueta de plantillas now. Una vez que el LocaleMiddleware ha determinado la preferencia del usuario, la deja disponible como request.LANGUAGE_CODE para cada objeto peticin. Eres libre de leer este valor en tu cdigo de vista. A continuacin un ejemplo simple:
def hello_world(request, count): if request.LANGUAGE_CODE == de-at : return HttpResponse( " You prefer to read Austrian German. " ) else: return HttpResponse( " You prefer to read another language. " )
Nota que con traduccin esttica (en otras palabras sin middleware) el idioma est en settings.LANGUAGE_CODE, mientras que con traduccin dinmica (con middleware) el mismo est en request.LANGUAGE_CODE.
(Nota que este ejemplo publica la vista en /i18n/setlang/). La vista espera ser llamada va el mtodo GET, con un parmetro language incluido en la cadena de consulta. Si el soporte para sesiones est activo, la vista guarda la opcin de idioma en la sesin del usuario. Caso contrario, guarda el idioma en una cookie django_language. Despus de haber jado la opcin de idioma Django redirecciona al usuario, para eso sigue el siguiente algoritmo: Django busca un parmetro next en la cadena de consulta. Si el mismo no existe o est vaci, Django intenta la URL contenida en la cabecera Referer. Si la misma est vaca por ejemplo, si el navegador de un usuario suprime dicha cabecera entonces el usuario ser redireccionado a / (la raz del sitio) como un ltimo recurso. Este es un fragmento de cdigo 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.0 }}">{{ lang.1 }}</option> { % endfor %} </select> <input type="submit" value="Go" /> </form>
235
El cdigo JavaScript no tiene acceso a una implementacin de gettext. El cdigo JavaScript no tiene acceso a los archivos .po o .mo; los mismos necesitan ser enviados desde el servidor. Los catlogos de traduccin para JavaScript deben ser mantenidos tan pequeos como sea posible. Django provee una solucin integrada para esos problemas: convierte las traducciones a JavaScript, de manera que puedas llamar a gettext y dems desde JavaScript.
Cada cadena en package debe seguir la sintaxis paquete separados por puntos de Python (el mismo formato que las cadenas en INSTALLED_APPS) y deben referirse a un paquete que contenga un directorio locale. Si especicas mltiples paquetes, todos esos catlogos son fusionados en un catlogo nico. esto es til si usas JavaScript que usa cadenas de diferentes aplicaciones. Puedes hacer que la vista sea dinmica colocando los paquetes en el patrn de la URL:
urlpatterns = patterns(, (r^jsi18n/(?P<packages>\S+?)/$, django.views.i18n.javascript_catalog), )
Con esto, especicas los paquetes como una lista de nombres de paquetes delimitados por un smbolo + en la URL. Esto es especialmente til si tus pginas usan cdigo de diferentes aplicaciones, este cambia frecuentemente y no deseas tener que descargar un nico gran catlogo. Como una medida de seguridad, esos valores pueden solo tomar los valores django.conf o cualquier paquete de la variable de conguracin INSTALLED_APPS.
Esta es la forma en la que el sitio de administracin obtiene el catlogo de traducciones desde el servidor. Una vez que se ha cargado el catlogo, tu cdigo JavaScript puede usar la interfaz estndar gettext para acceder al mismo:
document.write(gettext( this is to be translated ));
%(count)s
%(count)s
objects , d.co
237
La funcin interpolate admite tanto interpolacin posicional como interpolacin con nombres. De manera que el cdigo de arriba podra haber sido escrito de la siguiente manera:
s = interpolate(ngettext( this is %s object , this are %s objects , 11), [11]);
La sintaxis de interpolacin se tom prestada de Python. Sin embargo, no debes exagerar con el uso de la interpolacin de cadenas esto sigue siendo JavaScript as que el cdigo tendr que realizar mltiples sustituciones de expresiones regulares. Esto no es tan rpido como la interpolacin de cadenas en Python, de manera que deberas reservarlo para los casos en los que realmente lo necesites (por ejemplo en combinacin con ngettext para generar pluralizaciones en forma correcta).
Esto crea o actualiza el catlogo de traduccin para JavaScript para alemn. Luego de haber actualizado catlogos, slo ejecuta compile-messages.py de la misma manera que lo haces con los catlogos de traduccin normales de Django.
19.9 Qu sigue?
Este captulo esencialmente concluye nuestra cobertura de las caractersticas de Django. Deberas conocer lo suciente para comenzar a producir tus propios sitios usando Django. Sin embargo, escribir el cdigo es solo el primer paso de la instalacin de un sitio Web exitoso. Los siguientes dos captulos cubren las cosas que necesitars conocer si deseas que tu sitio sobreviva en el mundo real. El Captulo 19 trata cmo puedes hacer para hace que tus sitios y tus usuarios estn seguros ante atacantes maliciosos y el Captulo 20 detalla cmo instalar una aplicacin Django en uno o varios servidores.
238
CAPTULO 20
Internet puede ser un lugar aterrador. En estos tiempos, los papelones de seguridad con alta exposicin pblica parecen ser cosa de todos los das. Hemos visto virus propagarse con una velocidad asombrosa, ejrcitos de computadoras comprometidas ser empuados como armas, una interminable carrera armamentista contra los spammers, y muchos, muchos reportes de robos de identidad de sitios Web hackeados. Parte de las tareas de un desarrollador Web es hacer lo que est en sus manos para combatir esas fuerzas de la oscuridad. Todo desarrollador Web necesita considerar la seguridad como un aspecto fundamental de la programacin Web. Desafortunadamente, se da el caso de que implementar la seguridad es difcil los atacantes slo necesitan encontrar una nica vulnerabilidad, pero los defensores deben proteger todas y cada una. Django intenta mitigar esta dicultad. Est diseado para protegerte automticamente de muchos de los errores de seguridad comunes que cometen los nuevos (e incluso los experimentados) desarrolladores Web. Aun as, es importante entender de qu se tratan dichos problemas, cmo es que Django te protege, y esto es lo ms importante los pasos que puedes tomar para hacer tu cdigo aun ms seguro. Antes, sin embargo, una importante aclaracin: No es nuestra intencin presentar una gua denitiva sobre todos los exploits de seguridad Web conocidos, y tampoco trataremos de explicar cada vulnerabilidad en una forma completa. En cambio, presentaremos una breve sinopsis de problemas de seguridad que son relevantes para Django.
Nunca sabes quin est del otro lado de esa conexin HTTP. Podra tratarse de uno de tus usuarios, pero con igual facilidad podra tratarse de un vil cracker buscando un resquicio. Cualquier dato de cualquier naturaleza que arriba desde el navegador necesita ser tratado con una generosa dosis de paranoia. Esto incluye tanto datos que se encuentran in band (por ejemplo enviados desde formularios Web) como out of band (por ejemplo cabeceras HTTP, cookies, y otra informacin de peticin). Es trivial falsicar los metadatos de la peticin que los navegadores usualmente agregan automticamente. Todas las vulnerabilidades tratadas en este captulo derivan directamente de conar en datos que arriban a travs del cable y luego fallar a la hora de limpiar esos datos antes de usarlos. Debes convertir en una prctica general el preguntarte De donde vienen estos datos?
239
%s ; " % username
Nota: En este ejemplo, y en todos los ejemplos similares del tipo no hagas esto que siguen, hemos omitido deliberadamente la mayor parte del cdigo necesario para hacer que el mismo realmente funcione. No queremos que este cdigo sirva si accidentalmente alguien lo toma fuera de contexto y lo usa. A pesar de que a primera vista eso no parece peligroso, realmente lo es. Primero, nuestro intento de proteger nuestra lista de emails completa va a fallar con una consulta construida en forma ingeniosa. Pensemos acerca de qu sucede si un atacante escribe OR a=a" en la caja de bsqueda. En ese caso, la consulta que la interpolacin construir ser:
SELECT * FROM user_contacts WHERE username = OR a = a;
Debido a que hemos permitido SQL sin proteccin en la string, la clusula OR agregada por el atacante logra que se retornen todas los registros. Sin embargo, ese es el menos pavoroso de los ataques. Imaginemos qu sucedera si el atacante enva ; DELETE FROM user_contacts WHERE a = a". Nos encontraramos con la siguiente consulta completa:
SELECT * FROM user_contacts WHERE username = ; DELETE FROM user_contacts WHERE a = a;
20.2.1 La solucin
Aunque este problema es insidioso y a veces difcil de detectar la solucin es simple: nunca confes en datos provistos por el usuario y siempre escapa el mismo cuando lo conviertes en SQL. La API de base de datos de Django hace esto por ti. Escapa automticamente todos los parmetros especiales SQL, de acuerdo a las convenciones de uso de comillas del servidor de base de datos que ests usando (por ejemplo, PostgreSQL o MySQL). Por ejemplo, en esta llamada a la API:
foo.get_list(bar__exact= " OR 1=1 " )
240
que es completamente inocua. Esto se aplica a la totalidad de la API de base de datos de Django, con un par de excepciones: El argumento where del mtodo extra() (ver Apndice C). Dicho parmetro acepta, por diseo, SQL crudo. Consultas realizadas a mano usando la API de base de datos de nivel ms bajo. En tales casos, es fcil mantenerse protegido. para ello evita realizar interpolacin de strings y en cambio usa parmetros asociados (bind parameters). Esto es, el ejemplo con el que comenzamos esta seccin debe ser escrito de la siguiente manera:
from django.db import connection def user_contacts(request): user = request.GET[ username ] sql = " SELECT * FROM user_contacts WHERE username = cursor = connection.cursor() cursor.execute(sql, [user]) # ... do something with the results
%s ; "
El mtodo de bajo nivel execute toma un string SQL con marcadores de posicin %s y automticamente escapa e inserta parmetros desde la lista que se le provee como segundo argumento. Cuando construyas SQL en forma manual hazlo siempre de esta manera. Desafortunadamente, no puedes usar parmetros asociados en todas partes en SQL; no son permitidos como identicadores (esto es, nombres de tablas o columnas). As que, si, por ejemplo, necesitas construir dinmicamente una lista de tablas a partir de una variable enviada mediante POST, necesitars escapar ese nombre en tu cdigo. Django provee una funcin, django.db.backend.quote_name, la cual escapar el identicador de acuerdo al esquema de uso de comillas de la base de datos actual.
Esta vista simplemente lee un nombre desde un parmetro GET y pasa dicho nombre a la plantilla hello.html. Podramos escribir una plantilla para esta vista de la siguiente manera:
<h1>Hello, {{ name }}!</h1>
241
<h1>Hello, <i>Jacob</i>!</h1>
Obviamente, un atacante no usar algo tan inofensivo como etiquetas <i>; podra incluir un fragmento completo de HTML que se apropiara de tu pgina insertando contenido arbitrario. Este tipo de ataques ha sido usado para engaar a usuarios e inducirlos a introducir datos en lo que parece ser el sitio Web de su banco, pero en efecto es un formulario saboteado va XSS que enva su informacin bancaria a un atacante. El problema se complica aun ms si almacenas estos datos en la base de datos y luego la visualizas en tu sitio. Por ejemplo, en una oportunidad se encontr que MySpace era vulnerable a un ataque XSS de esta naturaleza. Un usuario haba insertado JavaScript en su pgina de perl, dicho cdigo agregana lo agregaba a la lista de amigos de todos los usuarios que visitaran su pgina de perl. En unos pocos das lleg a tener millones de amigos. Ahora, esto podra sonar relativamente inofensivo, pero no olvides que este atacante haba logrado que su cdigo no el cdigo de MySpace se ejecutara en tu computadora. Esto viola la conanza asumida acerca de que todo el cdigo ubicado en MySpace es realmente escrito por MySpace. MySpace fue muy afortunado de que este cdigo malicioso no hiciera cosas como borrar automticamente las cuentas de los usuarios que lo ejecutaran, o cambiar sus contraseas, o inundar el sitio con spam, o cualquiera de los otros escenarios de pesadilla que esta vulnerabilidad hace posibles.
20.3.1 La solucin
La solucin es simple: siempre escapa todo el contenido que pudiera haber sido enviado por un usuario. Si simplemente reescribiramos nuestra plantilla de la siguiente manera:
<h1>Hello, {{ name|escape }}!</h1>
ya no seramos vulnerables. Debes usar siempre la etiqueta escape (o algo equivalente) cuando visualizas en tu sitio contenido enviado por el usuario. Porqu simplemente Django no hace esto por m? Modicar Django para que escape automticamente todas las variables visualizadas en plantillas es un tpico de frecuente tratamiento en la lista de correo de desarrollo de Django. Hasta ahora, las plantillas Django han evitado este comportamiento debido a que esto cambia sutilmente algo que debera ser un comportamiento no complejo (la visualizacin de variables). Es un asunto no trivial y una decisin de compromiso difcil de evaluar. Agregando comportamiento implcito y oculto va contra los ideales de base de Django (y los de Python), pero la seguridad es igual de importante. Todo esto nos lleva, entonces, 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. Es una buena idea revisar la documentacin ocial de Django para conocer las novedades respecto a las caractersticas de Django; esta ser siempre ms actual que este libro, especialmente que la versin impresa. Aun si Django incorpora esta caracterstica debes formar el hbito de preguntarte, en todo momento, De donde provienen estos datos?. Ninguna solucin automtica proteger tu sitio de ataques XSS el 100 % del tiempo.
242
Django incluye herramientas para proteger ante este tipo de ataques. Tanto el ataque en s mismo como dichas herramientas son tratados con gran detalle en el Captulo 14.
20.5.1 La solucin
Existe un nmero de principios generales que pueden protegerte de estos ataques: Nunca permitas que exista informacin sobre sesiones contenida en las URLs. El framework de sesiones de Django (ver Captulo 12) simplemente no permite que las URLs contengan sesiones.
243
No almacenes datos en cookies en forma directa; en cambio, almacena un identicador de sesin que est relacionado a datos de sesin almacenados en el back-end. Si usas el framework de sesiones incluido en Django (o sea request.session), eso es manejado en forma automtica. La nica cookie que usa el framework de sesiones es un identicador de sesin; todos los datos de la sesiones se almacenan en la base de datos. Recuerda escapar los datos de la sesin si los visualizas en la plantilla. Revisa la seccin 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. Debes considerar la informacin de sesiones como datos creados por el usuario. Previene la falsicacin de de identicadores de sesin por parte de un atacante siempre que sea posible. A pesar de que es prcticamente imposible detectar a alguien que se ha apropiado de un identicador de sesin, Django incluye proteccin contra un ataque de sesiones de fuerza bruta. Los identicadores de sesin se almacenan como hashes (en vez de nmeros secuenciales) lo que previene un ataque por fuerza bruta, y un usuario siempre obtendr un nuevo identicador de sesin si intenta usar uno no existente, lo que previene la session xation. Nota que ninguno de estos principios y herramientas previene ante ataques man-in-the-middle. Dichos tipos de ataques son prcticamente imposibles de detectar. Si tu sitio permite que usuarios identicados visualicen algn tipo de datos importantes debes, siempre, publicar dicho sitio va HTTPS. Adicionalmente, si tienes un sitio con SSL, debes asignar a la variable de conguracin SESSION_COOKIE_SECURE el valor True; esto har que Django enve las cookies de sesin va HTTPS.
Como en la inyeccin de SQL, si conamos en la lnea de asunto enviada por el usuario, estaremos permitindole construir un conjunto malicioso de cabeceras, y podr usar nuestro formulario de contacto para enviar spam.
20.6.1 La solucin
Podemos prevenir este ataque de la misma manera en la que prevenimos la inyeccin de SQL: escapando o vericando siempre el contenido enviado por el usuario. Las funciones de mail incluidas en Django (en django.core.mail) simplemente no permiten saltos de lnea en ninguno de los campos usados para construir cabeceras (las direcciones de y para, ms el 244 Captulo 20. Captulo 19: Seguridad
asunto). Si intentas usar django.core.mail.send_mail con un asunto que contenga saltos de lnea, Django arrojar una excepcin BadHeaderError. Si no usas las funciones de email de Django para enviar email, necesitars asegurarte de que los saltos de lnea en las cabeceras o causan un error o son eliminados. Podras querer examinar la clase SafeMIMEText en django.core.mail para ver cmo implementa esto Django.
A pesar que parece que la vista restringe el acceso a archivos que se encuentren ms all que BASE_PATH (usando os.path.join), si la atacante enva un filename que contenga .. (esto es, dos puntos, una notacin corta para el directorio padre), podra acceder a archivos que se encuentren ms arriba que BASE_PATH. De all en ms es slo una cuestin de tiempo el hecho que descubra el nmero correcto de puntos para acceder exitosamente, por ejemplo a ../../../../../etc/passwd. Todo aquello que lea archivos sin el escaping adecuado es vulnerable a este problema. Las vistas que escriben archivos son igual de vulnerables, pero las consecuencias son doblemente calamitosas. Otra permutacin de este problema yace en cdigo que carga mdulos dinmicamente a partir de la URL u otra informacin de la peticin. Un muy pblico ejemplo se present en el mundo de Ruby on Rails. Con anterioridad a mediados del 2006, Rails usaba URLs como http://example.com/person/poke/1 directamente para cargar mdulos e invocar mtodos. El resultado fu que una URL cuidadosamente construida poda cargar automticamente cdigo arbitrario, incluso un script de reset de base de datos!
20.7.1 La solucin
Si tu cdigo necesita alguna vez leer o escribir archivos a partir de datos ingresados por el usuario, necesitas limpiar muy cuidadosamente la ruta solicitada para asegurarte que un atacante no pueda escapar del directorio base ms all del cual ests restringiendo el acceso. Nota: Nunca debes escribir cdigo que pueda leer cualquier rea del disco! Un buen ejemplo de cmo hacer este escaping yace en la vista de publicacin de contenido estticos (en django.view.static). Este es el cdigo relevante:
import os import posixpath # ... path = posixpath.normpath(urllib.unquote(path))
245
newpath = for part in path.split( / ): if not part: # strip empty path components continue drive, part = os.path.splitdrive(part) head, part = os.path.split(part) if part in (os.curdir, os.pardir): # strip . and .. in path continue newpath = os.path.join(newpath, part).replace( \\ , / )
Django no lee archivos (a menos que uses la funcin static.serve, pero en ese caso est protegida por el cdigo recin mostrado), as que esta vulnerabilidad no afecta demasiado el cdigo del ncleo. Adicionalmente, el uso de la abstraccin de URLconf signica que Django solo cargar cdigo que le hayas indicado explcitamente que cargue. No existe manera de crear una URL que cause que Django cargue algo no mencionado en una URLconf.
20.8.1 La solucin
Django tiene un sencillo control que gobierna la visualizacin de esos mensajes de error. Si se ja la variable de conguracin DEBUG al valor True, los mensajes de error sern visualizados en el navegador. De otra forma, Django retornar un mensaje HTTP 500 (Error interno del servidor) y renderizar una plantilla de error provista por ti. Esta plantilla de error tiene el nombre 500.html y debe estar situada en la raz de uno de tus directorios de plantillas. Dado que los desarrolladores aun necesitan ver los errores que se generan en un sitio en produccin, todos los errores que se manejen de esta manera dispararn el envo de un email con el traceback completo a las direcciones de correo conguradas en la variable ADMINS. Los usuarios que implementen en conjunto con Apache y mod_python deben tambin asegurarse que tienen PythonDebug Off en sus archivos de conguracin de Apache; esto suprimir cualquier error que pudiera ocurrir aun antes de que Django se haya cargado.
246
20.10 Qu sigue?
En el prximo captulo, nalmente trataremos las sutilezas de la implementacin de Django: como lanzar un sitio de produccin y como dotarlo de escalabilidad.
247
248
CAPTULO 21
A lo largo de este libro, hemos mencionado algunos objetivos que conducen el desarrollo de Django. Facilidad de uso, amigabilidad para nuevos programadores, abstraccin de tareas repetitivas todos esas metas marcaron el camino de los desarrolladores. Sin embargo, desde la concepcin de Django, ha existido siempre otro objetivo importante: Django debera ser fcil de implementar, y debera poder servir una gran cantidad de trco con recursos limitados. Las motivaciones para este objetivo se vuelven evidentes cuando observas el trasfondo de Django: un pequeo peridico en Kansas difcilmente pueda costear hardware de servidor de ltima tecnologa, por lo que los desarrolladores originales de Django trataron de extraer el mximo desempeo posible de los escasos recursos disponibles. De hecho, por aos los desarrolladores de Django actuaron como sus propios administradores de sistema ya que simplemente no haba suciente harware como para necesitar administradores dedicados a esa tarea incluso manejando sitios con decenas de millones de entradas por da. Como Django se volvi un proyecto open source, este enfoque en el desempeo y la facilidad de implementacin se torn importante por diferentes razones: los desarrolladores acionados tienen los mismos requerimientos. Los individuos que quieren usar Django estn encantados de saber que pueden hospedar un sitio con trco entre pequeo y mediano por menos de u$s 10 mensuales. Pero ser capaz de escalar hacia abajo es solamente la mitad de la batalla. Django tambin debe ser capaz de escalar hacia arriba para conocer las necesidades de grandes empresas y corporaciones. Aqu, Django adopta una losofa comn entre los grupos de software del tipo LAMP que suele llamarse shared nothing (nada compartido). Qu es LAMP? El acrnimo LAMP fue originalmente acuado 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 programacin) A lo largo del tiempo, el acrnimo se ha vuelto ms una referencia a la losofa de este tipo de agrupamiento de software que a cualquiera de estos en particular. Por ello, aunque Django usa Python y
249
es agnstico respecto al motor de base de datos a utilizar, las losofas probadas por los agrupamientos tipo LAMP permanece en la mentalidad de implementacin de Django. Han habido algunos (ms que nada cmicos) intentos de acuar acrnimos similares para describir los agrupamientos de tecnologa que usa Django. Los autores de este libro estn encariados con LAPD (Linux, Apache, PostgreSQL, y Django) o PAID (PostgreSQL, Apache, Internet, y Django). Usa Django y consigue un PAID! (N. de T.: En ingls, PAID signica pago).
250
Craigslist Facebook Google LiveJournal Slashdot Wikipedia Yahoo YouTube Parafraseando la famosa escena de Cuando Harry conoci a Sally...: Tendremos lo que ellos tienen!
251
Wainwright,
disponible
via
Apache: The Denitive Guide, Third Edition (OReilly, 2002) de Ben Laurie y Peter Laurie, disponible via http://www.djangoproject.com/r/books/apache-pra/
Asegurese de reemplazar misitio.settings por el DJANGO_SETTINGS_MODULE apropiado para tu sitio. Esto le dice a Apache, Usa mod_python para cualquier URL en / o bajo ella, usando el manejado mod_python de Django. Le pasa el valor de DJANGO_SETTINGS_MODULE de modo que mod_python conoce que conguracin utilizar. Nota que estamos usando la directiva <Location> y no <Directory>. Esta ltima se utiliza para apuntar a lugares de nuestra sistema de archivos, mientras que <Location> apunta a lugares en la estructura de la URL de un sitio web. <Directory> no tendra sentido aqu. Apache comunmente corre como un usuario diferente de tu usuario normal y puede tener una ruta y un sys.path distintos. Puedes necesitar decirle a mod_python cmo encontrar tu proyecto y a Django mismo:
PythonPath "[/ruta/al/proyecto, /ruta/a/django] + sys.path"
Tambin puedes agregar directivas como PythonAutoReload Off para ajustar la performance. Mira la documentacin de mod_python para un obtener un listado completo de opciones. Ten en cuenta que deberias congurar PythonDebug Off en un servidor de produccin. Si dejas PythonDebug On, tus usuarios vern feas trazas de error de Python si algo sale dentro de mod_python. Reinicia Apache, y cualquier peticin a tu sitio (o a tu host virtual si pusiste las directivas dentro de un bloque <VirtualHost>) ser servida por Django. Nota Si implementas Django en un subdirectorio esto es, en algun lugar ms profundo que / Django no recortar el prejo de la URL para tu URLpatterns. Entonces, si tu conguracin de Apache luce como esto:
<Location "/misitio/"> SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE misitio.settings PythonDebug On </Location>
252
entonces todos tus patrones de URL debern comenzar con /misitio/". Por esta razn es que usualmente recomendamos implementar Django sobre la raiz de tu dominio o host virtual. Alternativamente, simplemente puede hacer descender el nivel de tu URL usando una cua de URLconf:
urlpatterns = patterns( , ( r ^misitio/ , include( normal.root.urls )), )
Si necesitar poner dos instalaciones de Django sobre el mismo VirtualHost, necesitar prestar especial atencin para asegurarte de que el cach de cdigo de mod_python no mezcle las cosas. Usa la directiva PythonInterpreter para brindar diferentes directivas <Location> a interpretes distintos:
<VirtualHost *> ServerName www.ejemplo.com # ... <Location "/algo"> SetEnv DJANGO_SETTINGS_MODULE misitio.settings PythonInterpreter misitio </Location> <Location "/otracosa"> SetEnv DJANGO_SETTINGS_MODULE misitio.other_settings PythonInterpreter misitio_otro </Location> </VirtualHost>
Los valores de PythonInterpreter no importante realmente ya que se encuentran en dos bloques Location diferentes.
253
Si eres el tipo de programador que depuran dispersando sentencias print por el cdigo (nosotros somos), ten en cuenta que print no tiene efectos sobre mod_python; estas no aparecen en el log de Apache como pudras esperar. Si necesitas imprimir informacin de depuracin en una conguracin mod_python, probablemente quieras usar el paquete de registro de eventos estndar de Python (Pythons standard logging package). Hay ms informacin disponible en http://docs.python.org/lib/module-logging.html. Alternativamente, puedes agregar la informacin de depuracin a las plantillas de tu pgina.
Cambia Location a la URL raiz donde se encuentran tus archivos. Tambien puedes usar <LocationMatch> para comparar con una expresin regular. Por ejemplo, esto congura Django en la raiz del sitio pero deshabilitando Django para el subdirectorio media y cualquier URL que termine en .jpg, .gif, o .png:
<Location "/"> SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.settings </Location> <Location "/media/"> SetHandler None </Location> <LocationMatch "\.(jpg|gif|png)$"> SetHandler None </LocationMatch>
En todos estos casos, necesitars congurar la directiva DocumentRoot para que Apache sepa dnde encontrar tus archivos estticos.
254
255
Cada proceso Apache consigue una copia completa del motor de Apache, con todas las caractersticas de Apache que Django simplemente no aprovecha. Los procesos FastCGI, por otro lado, solo tienen el overhead de memoria de Python y Django. Debido a la naturaleza de FastCGI, tambin es posible tener procesos ejecutando bajo una cuenta de usuario diferente de la del proceso del servidor Web. Este es un buen benecio de seguridad es sistemas compartidos, dado que signica que puedes asegurar tu cdigo de otros usuarios. Antes de que puedas empezar a usar FastCGI con Django, necesitas instalar flup, una biblioteca Python para manejar FastCGI. Algunos usuarios han reportado pginas que explotaron con versiones antiguas de flup, por lo cual puedes querer utilizar la ltima versin SVN. Puedes conseguir flup en http://www.djangoproject.com/r/up/.
Si especicas help como nica opcin despus de runfcgi, se mostrar una lista de todas las opciones disponibles. Necesitars especicar un socket o si no host y port. Entonces, cuando congures tu servidor Web, solo necesitas apuntarlo al socket o host/port que especicaste cuando iniciaste el servidor FastCGI. Algunos ejemplos pueden ayudar a explicarlo: Ejecutar un servidor threaded en un puerto TCP:
./manage.py runfcgi method=threaded host=127.0.0.1 port=3033
Ejecutar sin demonizar (ejecutar en segundo plano) el proceso (es bueno para el debugging):
./manage.py runfcgi daemonize=false socket=/tmp/mysite.sock
Detener el Demonio FastCGI Si tienes el proceso ejecutando en primer plano, es fcil detenerlo: simplemente presiona Ctrl+C para detenerlo y salir del servidor FastCGI. Si ests tratando con procesos en segundo plano, necesitars recurrir al comando kill de Unix.
256
Si especicas la opcin pidfile en manage.py runfcgi, puedes detener el demonio FastCGI en ejecucin de esta forma:
kill cat $PIDFILE
donde $PIDFILE es el pidfile que especicaste. Para reiniciar con facilidad tu demonio FastCGI en Unix, pedes usar este breve script en la lnea de comandos:
#!/bin/bash # Replace these three settings. PROJDIR= "/home/user/myproject" PIDFILE= "$PROJDIR/mysite.pid" SOCKET= "$PROJDIR/mysite.sock" cd $PROJDIR if [ -f $PIDFILE ]; then kill cat -- $PIDFILE rm -f -- $PIDFILE fi exec /usr/bin/env - \ PYTHONPATH= "../python:.." \ ./manage.py runfcgi socket=$SOCKET pidfile=$PIDFILE
Connect to FastCGI via a TCP host/port: FastCGIExternalServer /home/user/public_html/mysite.fcgi -host 127.0.0.1:3033 En los dos casos, el directorio /home/user/public_html/ debe existir, aunque el archivo /home/user/public_html/mysite.fcgi no necesariamente tiene que existir. Es solo una URL usada por el servidor Web internamente un enganche para indicar que las consultas en esa URL deben ser manejadas por FastCGI. (Ms sobre esto en la siguiente seccin.)
257
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 patrn. PAra hacer esto, usa el mdulo mod_rewrite y reescribe las URLs hacia mysite.fcgi (o donde hayas especicado en la directiva FastCGIExternalServer, como se explic en la seccin anterior). En este ejemplo, le decimos a Apache que use FastCGI para manejar cualquier consulta que no represente un archivo del sistema de archivos y no empiece con /media/. Probablemente ste sea el caso ms comn, si ests usando el sitio de administracin de Django:
<VirtualHost 12.34.56.78> ServerName example.com DocumentRoot /home/user/public_html Alias /media /home/user/python/django/contrib/admin/media RewriteEngine On RewriteRule ^/(media.*)$ /$1 [QSA,L] RewriteCond %{REQUEST_FILENAME} !-f RewriteRule ^/(.*)$ /mysite.fcgi/$1 [QSA,L] </VirtualHost>
Ejecutando Mltiples Sitios Django en Una Instancia lighttpd lighttpd te permite usar conguracin condicional para permitir la conguracin personalizada para cada host. Para especicar mltiples sitios FastCGI, solo agrega un bloque condicional en torno a tu conguracin FastCGI para cada sitio:
258
# If the hostname is www.example1.com... $HTTP["host"] == "www.example1.com" { server.document-root = "/foo/site1" fastcgi.server = ( ... ) ... } # If the hostname is www.example2.com... $HTTP["host"] == "www.example2.com" { server.document-root = "/foo/site2" fastcgi.server = ( ... ) ... }
Puedes tambin ejecutar mltiples instalaciones de Django en el mismo sitio simplemente especicando mltiples entradas en la directiva fastcgi.server. Agrega un host FastCGI para cada una.
Despus, crea un pequeo script que le diga a Apache como iniciar tu programa FastCGI. Crea un archivo mysite.fcgi, colcalo en tu directorio Web, y asegrate de hacerlo ejecutable:
#!/usr/bin/python import sys, os # Add a custom Python path. sys.path.insert(0, " /home/user/python " ) # Switch to the directory of your project. (Optional.) # os.chdir("/home/user/myproject") # Set the DJANGO_SETTINGS_MODULE environment variable. os.environ[ DJANGO_SETTINGS_MODULE ] = " myproject.settings " from django.core.servers.fastcgi import runfastcgi runfastcgi(method= " threaded " , daemonize= " false " )
259
Reiniciando el Server Iniciado Si cambias cualquier cdigo Python en tu sitio, necesitars decirle a FastCGI que el cdigo ha cambiado. Pero no hay necesidad de reiniciar Apache en este caso the. Slo volver a subir mysite.fcgi o editar el archivo de manera que la fecha y hora del archivo cambien. Cuando Apache ve que el archivo ha sido actualizado, reiniciar tu aplicacin Django por ti. Si tienen acceso a la lnea de comandos en un sistema Unix system, puedes hacer esto fcilmente usando el comando touch:
touch mysite.fcgi
21.5 Escalamiento
Ahora que sabes como tener a Django ejecutando en un servidor simple, veamos como puedes escalar una instalacin Django. Esta seccin explica como puede escalar un sitio desde un servidor nico a un cluster de gran escala que pueda servir millones de hits por hora. Es importante notar, sin embargo, que cada sitio grande es grande de diferentes formas, por lo que escalar es cualquier cosa menos una operacin de una solucin nica para todos los casos. La siguiente cobertura debe ser suciente para mostrat el principio general, y cuando sea posible, trateremos de sealar donde se puedan elegir distintas opciones. Primero, haremos una buena presuposicin, y hablaremos exclusivamente acerca de escalamiento bajo Apache y mod_python. A pesar de que conocemos vario casos exitosos de desarrollos FastCGI medios y grandes, estamos mucho ms familiarizados con Apache.
Figura 20-1: conguracin de Django en un servidor nico . Esto funciona bien para sitios pequeos y medianos, y es relativamente barato puedes instalar un servidor nico diseado para Django por menos de 3,000 dlares. Sin embargo, a medida que el trco se incemente, caers rpidamente en contencin de recursos entre las diferentes piezas de software. Los servidores de base de datos y los servidores Web adoran tener el 260 Captulo 21. Captulo 20: Puesta en marcha de Django en un servidor
servidor entero para ellos, y cuando corren en el mismo servidos siempre terminan peleando por los mismos recursos (RAM, CPU) que preeren monopolizar. Esto se resuelve fcilmente moviendo el servidor de base de datos a una segunda mquina, como se explica en la siguiente seccin.
Figura 20-2: Moviendo la base de datos a un servidor dedicado. Aqui es donde empezamos a movernos hacia lo que usualmente se llama arquitectura n-tier. No te asustes por la terminiloga slo se reere al hecho de que diferentes tiers de la pila Web separadas en diferentes mquinas fsicas. A esta altura, si anticipas que en algn momento vas a necesitar crecer ms all de un servidor de base de datos nico, probablemente sea una buena idea empezar a pensar en pooling de conexiones y/o replicacin de bases de datos. Desafortunadamente, no hay suciente espacio para hacerle justicia a estos temas en este libro, as que vas a necesitar consultar la documentacin y/o a la comunidad de tu base de datos para ms informacin.
21.5. Escalamiento
261
el siguiente paso es separar los medios esto es, todo lo que no es generado por una vista de Django a un servidor dedicado (ver Figura 20-3).
Figura 20-3: Separando el servidor de medios. Idealmente, este servidor de medios debera correr un servidor Web desnudo, optimizado para la entrega de medios estticos. lighttpd y tux (http://www.djangoproject.com/r/tux/) son dos excelentes elecciones aqu, pero un servidor Apache bien pelado tambin puede funcionar. Para sitios pesados en contenidos estticos (fotos, videos, etc.), moverse a un servidor de medios separado es doblemente importante y debera ser el primer paso en el escalamiento hacia arriba. De todos modos, este paso puede ser un poco delicado. El administrador de Django necesita poder escribir medios subidos en el servidor de medios. (la conguracin de MEDIA_ROOT controla donde se esciben estos medios). Si un medio habita en otro servidor, de todas formas necesitas organizar una forma de que esa escritura se pueda hacer a traves de la red. La manera ms fcil 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). Si los montas en la misma ubicacin apuntada por MEDIA_ROOT, el uploading de medios Simplemente Funciona.
262
ten algunos balanceadores de carga por software de alta calidad que son open source. mod_proxy de Apache es una opcin, pero hemos encontrado que Perlbal (http://www.djangoproject.com/r/perlbal/) es simplemente fantstico. Es un balanceador de carga y proxy inverso escrito por las mismas personas que escribieron memcached (ver Captulo 13_). Si ests usando FastCGI: Puedes realizar este mismo paso distribucin y balance de carga separando los servidores Web frontend y los procesos FastCGI back-end en diferentes mquinas. El servidor front-end se convierte esencialmente en el balanceador de carga, y los procesos FastCGI back-end reemplaza a los servidores Apache/mod_python/Django. Con los servidores Web en cluster, nuestra arquitectura en evolucin empieza a verse ms compleja, como se ve en la Figura 20-4.
Figura 20-4: Conguracin de un server redundante con balance de carga. Observar que en el diagrama nos referimos a los servidores Web como el cluster para indicar que el numero de servidores basicamente es variable. Una vez que tienes un balanceador de carga en el frente, pueder agregar y eliminar servidores Web back-end sin perder un segundo fuera de servicio.
21.5. Escalamiento
263
PostgreSQL deberan mirar a Slony (http://www.djangoproject.com/r/slony/) y pgpool (http://www.djangoproject.com/r/pgpool/) para replicacin y pooling de conexiones, respectivamente. Si un solo balanceador de carga no es suciente, puedes agregar ms mquinas balanceadoras de carga y distribuir entre ellas usando DNS round-robin. Si un servidor nico de medios no es suciente, puedes agregar ms servidores de medios y distribuir la carga con tu cluster de balanceadores de carga. Si necesitas ms almacenamiento cache, puedes agregar servidores de cache dedicados. En cualquier etapa, si un cluster no tiene buena performance, puedes agregar ms servidores al cluster. Despus de algunas de estas iteraciones, una arquitectura de gran escala debe verse como en la Figura 20-5.
Figura 20-5. Un ejemplo de conguracin de Django de gran escala. A pesar de que mostramos solo dos o tres servidores en cada nivel, no hay un lmite fundamental a cuantos puedes agregar. Una vez que haz llegado a este nivel, te quedan pocas opciones. El Apndice A tiene alguna informacin proveniente de desarrolladores responsables de algunas instalaciones Django de gran escala. Si ests planicando un sitio Django de alto trco, es una lectura recomendada.
264
Desafortunadamente, el ajuste de performance es ms un arte que una ciencia, y es aun ms dicil de escribir sobre eso que sobre escalamiento. Si ests pensando seriamente en desplegar una aplicacin Django de gran escala, debers pasar un un buen tiempo aprendiendo como ajustar cada pieza de tu stack. Las siguientes secciones, sin embargo, presentan algunos tips especcos del ajuste de performance de Django que hemos descubiero a traves de los aos.
Y tambin asegrate de unirte a la comunidad Django. Tus humildes autores son solo dos miembros de un grupo increblemente activo y creciente de desarrolladores Django. Nuestra comunidad tiene una enorme cantidad de experiencia colectiva para ofrecer.
21.7 Qu sigue?
Has alcanzado el nal de nuestro programa regular. Los siguientes apndices 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, ya sea un pequeo juguete para t o el prximo Google. Apendice
266
CAPTULO 22
Apndice
Casos de estudio Referencia de la denicin de modelos Referencia de la API de base de datos Referencia de las vistas genricas Variables de conguracin Etiquetas de plantilla y ltros predenidos El utilitario django-admin Objetos Peticin y Respuesta
267
268
CAPTULO 23
Inicio:
INTRODUCCION A DJANGO EMPEZANDO LOS PRINCIPIOS DE LAS PAGINAS WEB DINAMICAS EL SISTEMA DE PLANTILLAS DE DJANGO INTERACTUAR CON UNA BASE DE DATOS EL SITIO DE ADMINISTRACION DE DJANGO PROCEMIENTO DE FORMULARIOS VISTAS AV ANZADAS Y URLCONFS VISTAS GENERICAS EXTENDIENDO EL SISTEMA DE PLANTILLAS GENERACION DE CONTENIDO NO HTML SESIONES, USUARIOS E INSCRIPCIONES CACHE BATERIAS INCLUIDAS, SUBFRAMEWORKS INCLUIDOS MIDDLEWARE INTEGRACION CON BASE DE DATOS Y APLICACIONES EXISTENTES EXTENDIENDO LA INTERFAZ DE ADMINISTRACION DE DJANGO INTERNACIONALIZACION SEGURIDAD IMPLEMENTANDO DJANGO
269
270
CAPTULO 24
Para ayudar a responder las preguntas acerca de cmo funciona Django en el mundo real, hablamos (bueno, nos escribimos) con un puado de personas que tienen sitios completos y funcionales desarrollados en Django. La mayor parte de este apndice son sus palabras, que han sido ligeramente editadas para mayor claridad.
24.1 Elenco
Conozcamos a nuestros personajes y sus proyectos. Ned Batchelder es el ingeniero principal de Tabblo.com. Tabblo comenz su vida como una herramienta para contar historias basadas en fotos compartidas, pero recientemente fue comprado por Hewlett-Packard para propsitos de mayor alcance: HP vio valor real en nuestro estilo de desarrollo web, y en la forma en que tendimos puentes entre el mundo virtual y el mundo fsico. Nos adquirieron de modo que pudiramos llevar esa tecnologa a otros sitios en la Web. Tabblo.com todava es un gran sitio para contar historias, pero ahora tambin estamos trabajando en separar componentes y reutilizar las piezas ms importantes de nuestra tecnologa. Johannes Beigel es uno de los principales desarrolladores en Brainbot Technologies AG. El sitio Django ms orientado al pblico de Brainbot es http://pediapress.com/, donde se pueden solicitar versiones impresas de artculos de Wikipedia. El equipo de Johannes actualmente est trabajando en un programa de gestin de conocimiento para empresas conocido como Brainler. Johannes nos cuenta que Brainler [...] es una solucin de software para gestionar, buscar, categorizar y compartir informacin de fuentes distribuidas. Est construido para el uso empresarial tanto en entornos de intranet como internet y es altamente escalable y personalizable. El desarrollo de los conceptos y componentes medulares comenz en el 2001. Recientemente hemos rediseado/reimplementado el servidor y el cliente web, que [ahora] est basado en Django. David Cramer es el desarrollador principal de Curse, Inc. l desarroll Curse.com, un sitio para devotos a los videojuegos multijugador masivos como World of Warcraft, Ultima Online, y otros. Curse.com es uno de los sitios sobre Django ms grandes de Internet: Tenemos aproximadamente entre 60 y 90 millones de visitas a la pgina en promedio por mes,y tuvimos picos de ms de 130 millones de visitas (en un mes) usando Django. Somos un sitio web muy dinmico y orientado al usuario especcamente enfocado en juegos multijugador masivos, y uno de los sitios web ms grandes acerca de World of Warcraft globalmente. Nuestro sitio web se estableci a principios de 2005, y desde nales de 2006 nos hemos estado expandiendo a juegos ms all de World of Warcraft. 271
Christian Hammond es un ingeniero senior en VMware (un desarrollador lder de software de virtualizacin). Tambin es el desarrollador principal de Review Board (http://www.reviewboard.org/), un sistema de revisin de cdigo basado en Web. Review Board naci como un proyecto interno de VMware, pero ahora es open source: A nales de 2006, David Trowbridge y yo discutamos el proceso que usabamos en VMware para manejar la revisin de cdigo. Antes de que la gente enviara cdigo al repositorio, se supona que enviaran un diff de los cambios a una lista de correo para ser revisado. Todo se manejaba por email, y por supuesto, se volvi difcil seguir las revisiones que necesitaban mayor atencin. Fue entonces que comenzamos a discutir potenciales soluciones para este problema. En vez de escribir mis ideas, las puse en cdigo. En poco tiempo, naci Review Board. Review Board ayuda a los desarrolladores, contribuidores y revisores a seguir la evolucin del cdigo propuesto a revisin y a mejorar la comunicacin entre unos y otros. En vez de hacer una vaga referencia a una parte del cdigo en un email, el revisor puede comentar directamente sobre el cdigo. El cdigo, junto a los comentarios, aparecer entonces en las revisiones, dando a los desarrolladores suciente contexto para trabajar ms rpidamente en los cambios que hagan falta. Review Board creci rpidamente en VMware. De hecho, mucho ms rpido de lo esperado. En unas pocas semanas, tuvimos diez equipos usando Review Board. Sin embargo, este proyecto no es interno para VMware. Se decidi desde el da uno que debera ser open source y estar disponible para ser usado por cualquier compaa o proyecto. Hicimos un anuncio de open source y habilitamos el sitio conjuntamente, que est disponible en http://www.review-board.org/. La respuesta a nuestro anuncio pblico fue tan impresionante como nuestro anuncio interno a VMware. En poco tiempo, nuestro servidor de demostracin recibi ms de 600 usuarios, y la gente comenz a contribuir al proyecto en s. Review Board no es la nica herramienta de revisin de cdigo del mercado, pero es la primera que hemos visto que es abierta y tiene el extenso conjunto de caractersticas en el que hemos trabajado para que incluya. Esperamos que esto repercuta en benecios de tiempo para muchos proyectos (open source o no).
272
menos incmodo, resultando en un cdigo mucho mejor para mantener, y que es mucho ms divertido para trabajar. David Cramer: Escuch sobre Django en el verano de 2006, momento en que estabamos listos para hacer un reacondicionamiento de Curse, y decidimos investigarlo. Quedamos muy impresionado de lo que poda hacer, y todos los aspectos donde poda ahorrarnos tiempo. Lo conversamos y al decidirnos por Django comenzamos a escribir la tercera revisio del sitio web casi inmediatamente. Christian Hammond: Haba jugado con Django en un par de pequeos proyectos y qued muy impresionado con l. Est basado en Python, del que soy un gran fantico, y esto lo hace fcil no slo para desarrollar sitios y aplicaciones web, sino que tambin mantiene el trabajo organizado y mantenible. Esto siempre es un problema en PHP y Perl. Basado en experiencias del pasado, no necesit pensar mucho para meterme con Django.
24.3 Comenzando
Como Django es una herramienta relativamente nueva, no hay muchos desarrolladores experimentados ah afuera que lo dominen ampliamente. Le preguntamos a nuestro panel cmo consiguieron que su equipo adquiriese velocidad con Django y qu consejos querran compartir con nuevos desarrolladores Django. Johannes Beigel: Luego de programar principalmente en C++ y Perl, nos cambiamos a Python y continuamos usando C++ para el cdigo computacionalmente intensivo. [Aprendimos Django mientras] trabajamos con el tutorial, navegando la documentacin para obtener una idea de lo que es posible (es fcil perderse muchas caracterstica si slo se sigue el tutorial), e intentando comprender los conceptos bsicos detrs del middleware, objetos request, modelos de base de datos, etiquetas de plantillas, ltros personalizados, los formularios, autorizacin, localizacin... Luego podramos revisar ms profundamente esos tpicos cuando realmente los necesitramos. David Cramer: La documentacin del sitio web es grandiosa. Pgate a ella. Christian Hammond: David y yo tenamos una experiencia previa con Django, aunque era limitada. Hemos aprendido mucho mientras desarrollbamos Review Board. Le aconsejara a los nuevos usuarios que lean la tan bien escrita documentacin de Django y el libro que ahora estn leyendo, ya que ambos han sido inestimables para nosotros. No tuvimos que sobornar a Christian para conseguir esa declaracin lo juramos!
24.3. Comenzando
273
El sitio anterior estaba escrito en PHP. Ir de PHP a Python fue grandioso programticamente. El nico detalle es que hay que tener mucho ms cuidado con la gestin de memoria [ya que los procesos Django permanecen mucho ms tiempo que los procesos PHP (que son simples ciclos)].
En mayo de 2007, Blizzard (los creadores de World of Warcraft) lanzaron otro parche bastante grande, como el que haban lanzado en diciembre cuando nuestro sitio fue lanzado en Django. La primer cosa que pas por nuestras cabezas fue, hey, si soportamos el aluvin de diciembre, esto de ninguna manera puede ser tan grande, deberamos estar bien. Perdimos cerca de 12 horas antes de que los servidores comenzaran a sentir el calor. La pregunta surgi de nuevo: Realmente era Django la mejor solucin para lo que nosotros queramos lograr? Gracias a todo el gran apoyo de la comunidad, y largas noches, pudimos implementar varios arreglos en caliente sobre el sitio durante esos das. Los cambios introducidos (que con suerte estarn incorporados a Django en el momento que este libro vea la luz) permiten que con completa tranquilidad, aquellos (no cualquiera) que tengan que lidiar con 300 peticiones web por segundo, puedan hacerlo, con Django. Christian Hammond: Django nos permiti construir Review Board bastante rpidamente forzndonos a estar organizados a travs de la separacin de URL, vistas y plantillas, y proveyndonos tiles componentes listos para usar, como la aplicacin de autenticacin, cacheo, y la abstraccin de base de datos. La mayor parte de esto funcion realmente bien para nosotros. Siendo un aplicacin web dinmica, tuvimos que escribir un montn de cdigo JavaScript. Esta es una rea en la que Django no ha podido ayudarnos realmente mucho. El soporte que provee Django para el uso de plantillas, etiquetas, ltros y formularios son grandiosos, pero no son fcilmente utilizables desde cdigo JavaScript. Hay veces que quisiramos usar una plantilla en particular o un ltro, pero no hay manera de usarlo desde JavaScript. Personalmente me gustara ver que se incorporen a Django algunas soluciones creativa para esto.
275
usuarios en VMware que usan Review Board todos los das nos proveen de un montn de feedback til y reportes de errores, que intentamos incorporar en el programa.
24.7 Implementacin
Los desarrolladores de Django toman la facilidad de implementacin y escalamiento muy seriamente, por lo que siempre estamos interesados en escuchar sobre ensayos y tribulaciones del mundo real. Ned Batchelder: Hemos usado cacheo tanto en la capa de consulta como de respuesta para agilizar los tiempos de respuesta. Tenemos una conguracin clsica: un multiplexor, varios servidores de aplicacin, un servidor de base de datos. Eso ha funcionado bien para nosotros, porque podemos usar cacheo sobre el servidor de aplicacin para evitar el acceso a la base de datos, y luego agregar tantos servidores de aplicacin como necesitemos para manejar la demanda. Johannes Beigel: Servidores Linux, preferentemente Debian, con muchos gigas de RAM. Lighttpd como servidor Web, Pound como front-end HTTPS y balanceador de carga si se necesita, y Memcached como sistema de cach. SQLite para pequeas bases de datos, Postgres si los datos crecen mucho, y cosas altamente especializadas de base de datos para nuestros componentes de bsqueda y de gestin de conocimiento. David Cramer: Nuestra estructura todava est para debatirse... (pero es esto actualmente): Cuando un usuario solicita el sitio se lo enva a un cluster de servidores Squid usando Lighttpd. All, los servidores verican si el usuario est registrado en el sistema. Si no lo est, se le sirve una pgina cacheada. Un usuario autenticado es reenviado a un cluster de servidores corriendo Apache2 con mod_python (cada uno con una gran cantidad de memoria), donde luego cada uno hace uso de en un sistema Memcached distribuido y un bestial servidor de base de datos MySQL. Los datos estticos, como archivos grandes o videos, (actualmente) estan hospedados en un servidor corriendo una instalacin mnima de Django usando lighttpd con fastcgi. Ms adelante, migraremos todos esos datos a un servicio similar al S3 de Amazon. Christian Hammond: Hay dos servidores de produccin en este momento. Uno est en VMware y consiste en una maquina virtual Ubuntu corriendo en VMware ESX. Usamos MySQL para la base de datos, Memcached para nuestro back-end de cacheo, y actualmente Apache para el servidor Web. Tenemos varios servidores potentes que pueden escalarse cuando lo requiramos. Tambin podemos mudar MySQL o Memcached a otra maquina virtual a medida que nuestra base de usuarios crece. El segundo servidor de produccin es el de Review Board mismo. La conguracin es casi idntica al anterior, excepto que la maquina virtual se basa en VMware Server.
276
CAPTULO 25
El Captulo 5 explica lo bsico de la denicin de modelos, y lo utilizamos en el resto del libro. Existe un enorme rango de opciones disponibles que no se han cubierto en otro lado. Este apndice explica toda opcin disponible en la denicin de modelos. A pesar de que estas APIs se consideran muy estables, los desarrolladores de Django agregan en forma consistente nuevos atajos y conveniencias a la denicin de modelos. Es buena idea chequear siempre la ltima documentacin online en http://www.djangoproject.com/documentation/
25.1 Campos
La parte ms importante de un modelo y la nica parte requerida de un modelo es la lista de campos de la base de datos que dene. Restricciones en el nombre de los campos Django pone solo dos restricciones en el nombre de los campos: 1. Un nombre de campo no puede ser una palabra reservada de Python, porque eso ocasionara un error de sintaxis en Python, por ejemplo:
class Example(models.Model): pass = models.IntegerField() # pass es una palabra reservada!
2. Un nombre de campo no puede contener dos o ms guiones bajos consecutivos, debido a la forma en que trabaja la sintaxis de las consultas de bsqueda de , por ejemplo:
class Example(models.Model): foo__bar = models.IntegerField() # foo__bar tiene dos guiones bajos!
Estas limitaciones se pueden manejar sin mayores problemas, dado que el nombre del campo no necesariamente tiene que coincidir con el nombre de la columna en la base de datos. Ver db_column, ms abajo. Las palabras reservadas de SQL, como join, where, o select, son permitidas como nombres de campo, dado que Django escapea todos los nombres de tabla y columna de la base de datos en cada consulta SQL subyacente. Utiliza la sintaxis de quoteo del motor de base de datos particular. Cada campo en tu modelo debe ser una instancia de la clase de campo apropiada. Django usa los tipos de clase Field para determinar algunas cosas:
277
El tipo de columna de la base de datos (ej.:, INTEGER, VARCHAR). El widget a usar en la interfaz de administracin de Django, si vas a usarla (ej., <input type="text">, <select>). Los requerimientos mnimos de validacin, que se usan en la interfaz de administracin de Django. A continuacin, una lista completa de las clases de campo, ordenadas alfabticamente. Los campos de relacin (ForeignKey, etc.) se tratan en la siguiente seccin.
25.1.1 AutoField
Un IntegerField que se incrementa automticamente de acuerdo con los IDs disponibles. Normalmente no necesitars utilizarlos directamente; se agrega un campo de clave primaria automticamente a tu modelo si no especicas una clave primaria.
25.1.2 BooleanField
Un campo Verdadero/Falso.
25.1.3 CharField
Un campo string, para cadenas cortas o largas. Para grandes cantidades de texto, usa TextField. CharField requiere un argumento extra, max_length, que es la longitud mxima (en caracteres) del campo. Esta longitud mxima es reforzada a nivel de la base de datos y en la validacin de Django.
25.1.4 CommaSeparatedIntegerField
Un campo de enteros separados por comas. Igual que en CharField, se requiere el argumento max_length.
25.1.5 DateField
Un campo de fecha. DateField tiene algunos argumentos opcionales extra, como se muestra en la Tabla B-1. ArguDescripcin mento auto_now Asigna automticamente al campo un valor igual al momento en que se guarda el objeto. Es til para las marcas de tiempo ltima modicacin. Observar que siempre se usa la fecha actual; no es un valor por omisin que se pueda sobreescribir. auto_now_add Asigna automticamente al campo un valor igual al momento en que se crea el objeto. Es til para la creacin de marcas de tiempo. Observar que siempre se usa la fecha actual; no es un valor por omisin que se pueda sobreescribir.
25.1.6 DateTimeField
Un campo de fecha y hora. Tiene las mismas opciones extra que DateField.
25.1.7 EmailField
Un CharField que chequea que el valor sea una direccin de email vlida. No acepta max_length; su max_length se establece automticamente en 75. 278 Captulo 25. Apndice B: Referencia de la denicin de modelos
25.1.8 FileField
Un campo de upload de archivos. Tiene un argumento requerido, como se ve en la Tabla B-2. ArguDescripcin mento upload_to ruta del sistema de archivos local que se agregar a la conguracin de MEDIA_ROOT Una para determinar el resultado de la funcin de ayuda get_<fieldname>_url() Esta ruta puede contener formato strftime (ver http://www.djangoproject.com/r/python/strftime/), que ser reemplazada por la fecha y hora del upload del archivo (de manera que los archivos subidos no llenen el directorio dada). El uso de un FileField o un ImageField en un modelo requiere algunos pasos: 1. En el archivo de conguracin (settings), es necesario denir MEDIA_ROOT con la ruta completa al directorio donde quieras que Django almacene los archivos subidos. (Por performance, estos archivos no se almacenan en la base de datos.) Denir MEDIA_URL con la URL pblica base de ese directorio. Asegurarse de que la cuenta del usuario del servidor web tenga permiso de escritura en este directorio. 2. Agregar el FileField o ImageField al modelo, asegurndose de denir la opcin upload_to para decirle a Django a cual subdirectorio de MEDIA_ROOT debe subir los archivos. 3. Todo lo que se va a almacenar en la base de datos es la ruta al archivo (relativa a MEDIA_ROOT). Seguramente preferirs usar la facilidad de la funcin get_<fieldname>_url provista por Django. Por ejemplo, si tu ImageField se llama mug_shot, puedes obtener la URL absoluta a tu image en un plantilla con {{object.get_mug_shot_url }}. Por ejemplo, digamos que tu MEDIA_ROOT es /home/media, y upload_to es photos/ %Y/ %m/ %d. La parte %Y/ %m/ %d de upload_to es formato strftime; %Y es el ao en cuatro dgitos, %m es el mes en dos digitos, y %d es el da en dos dgitos. Si subes un archivo el 15 de enero de 2007, ser guardado en /home/media/photos/2007/01/15. Si quieres recuperar el nombre en disco del archivo subido, o una URL que se reera a ese archivo, o el tamao del archivo, puedes usar los mtodos get_FIELD_filename(), get_FIELD_url(), y get_FIELD_size(). Ver el Apndice C para una explicacin completa de estos mtodos. Nota. Cualquiera sea la forma en que manejes tus archivos subidos, tienes que prestar mucha atencin a donde los ests subiendo y que tipo de archivos son, para evitar huecos en la seguridad. Valida todos los archivos subidos para asegurarte que esos archivos son lo que piensas que son. Por ejemplo, si dejas que cualquiera suba archivos ciegamente, sin validacin, a un directorio que est dentro de la raz de documentos (document root) de tu servidor web, alguien podra subir un script CGI o PHP y ejecutarlo visitando su URL en tu sitio. No permitas que pase!
25.1.9 FilePathField
Un campo cuyas opciones estn limitadas a los nombres de archivo en un cierto directorio en el sistema de archivos. Tiene tres argumentos especiales, que se muestran en la Tabla B-3.
25.1. Campos
279
Argumento path
Descripcin
Requerido; la ruta absoluta en el sistema de archivos hacia el directorio del cual este FilePathField debe tomar sus opciones (ej.: /home/images"). match Opcional; una expresin regular como string, que FilePathField usar para ltrar los nombres de archivo. Observar que la regex ser aplicada al nombre de archivo base, no a la ruta completa (ej.: "foo.*\.txt^", va a matchear con un archivo llamado foo23.txt, pero no con bar.txt o foo23.gif). recursive Opcional; True o False.El valor por omisin es False. Especica si deben incluirse todos los subdirectorios de path. Por supuesto, estos argumentos pueden usarse juntos. El nico gotcha potencial es que match se aplica sobre el nombre de archivo base, no la ruta completa. De esta manera, este ejemplo:
FilePathField(path= " /home/images " , match= " foo.* " , recursive=True)
va a matchear con /home/images/foo.gif pero no con /home/images/foo/bar.gif porque el match se aplica al nombre de archivo base (foo.gif y bar.gif).
25.1.10 FloatField
Un numero de punto otante, representado en Python por una instancia de float. Tiene dos argumentos requeridos, que se muestran en la Tabla B-4. Argumento max_digits decimal_places descripcin La cantidad mximo de dgitos permitidos en el nmero. La cantidad de posiciones decimales a almacenar con el nmero.
Por ejemplo, para almacenar nmeros hasta 999 con una resolucin de dos decimales, hay que usar:
models.FloatField(..., max_digits=5, decimal_places=2)
Y para almacenar nmeros hasta aproximadamente mil millones con una resolucin de diez dgitos decimales, hay que usar:
models.FloatField(..., max_digits=19, decimal_places=10)
25.1.11 ImageField
Similar a FileField, pero valida que el objeto subido sea una imagen vlida. Tiene dos argumentos opcionales extra, height_field y width_field, que si se utilizan, sern auto-rellenados con la altura y el ancho de la imagen cada vez que se guarde una instancia del modelo. Adems de los mtodos especiales get_FIELD_* que estn disponibles para FileField, un ImageField tiene tambin los mtodos get_FIELD_height() y get_FIELD_width(). stos estn documentados en el Apndice C. ImageField requiere la biblioteca Python Imaging Library (http://www.pythonware.com/products/pil/).
25.1.12 IntegerField
Un entero.
25.1.13 IPAddressField
Una direccin IP, en formato string (ej.: "24.124.1.30").
280
25.1.14 NullBooleanField
Similar a BooleanField, pero permite None/NULL como opciones. Usar ste en lugar de un BooleanField con null=True.
25.1.15 PhoneNumberField
Un CharField que chequea que el valor es un telfono vlido estilo U.S. (en formato XXX-XXX-XXXX). Nota: Si necesitas representar telfonos de otros pases, consulta el paquete django.contrib.localflavor para ver si ya estn incluidas las deniciones para tu pas.
25.1.16 PositiveIntegerField
Similar a IntegerField, pero debe ser positivo.
25.1.17 PositiveSmallIntegerField
Similar a PositiveIntegerField, pero solo permite valores por debajo de un lmite. El valor mximo 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 mximo positivo usualmente es 65,535.
25.1.18 SlugField
Slug es un trmino de la prensa. Un slug es una etiqueta corta para algo, que contiene solo letras, nmeros, guiones bajos o simples. Generalmente se usan en URLs. De igual forma que en CharField, puedes especicar max_length. Si max_length no est especicado, Django asume un valor por omisin de 50. Un SlugField implica db_index=True debido a que son los se usan principalmente para bsquedas en la base de datos. SlugField acepta una opcin extra, prepopulate_from, que es una lista de campos a partir de los cuales auto-rellenar el slug, va JavaScript, en el formulario de administracin del objeto:
models.SlugField(prepopulate_from=( " pre_name " , " name " ))
25.1.19 SmallIntegerField
Similar a IntegerField, pero solo permite valores en un cierto rango dependiente de la base de datos (usualmente -32,768 a +32,767).
25.1.20 TextField
Un campo de texto de longitud ilimitada.
25.1. Campos
281
25.1.21 TimeField
Un campo de hora. Acepta las mismas opciones de autocompletacin de DateField y DateTimeField.
25.1.22 URLField
Un campo para una URL. Si la opcin verify_exists es True (valor por omisin), se chequea la existencia de la URL dada (la URL carga y no da una respuesta 404). Como los otros campos de caracteres, URLField toma el argumento max_length. Si no se especica, el valor por omisin es 200.
25.1.23 USStateField
Una abreviatura de dos letras de un estado de U.S. Nota: Si necesitas representar otros pases o estados, busca en el paquete django.contrib.localflavor para ver si Django ya incluye los campos para tu localizacin.
25.1.24 XMLField
Un TextField que chequea que el valor sea un XML vlido que matchea con un esquema dado. Tiene un argumento requerido, schema_path, que es la ruta en el sistema de archivos a un esquema RELAX NG (http://www.relaxng.org/) contra el cual validar el campo. Requiere jing (http://thaiopensource.com/relaxng/jing.html) para validar el XML.
25.2.1 null
Si est en True, Django almacenar valores vacos como NULL en la base de datos. El valor por omisin es False. Observar que los valores de string nulo siempre se almacenan como strings vacos, no como NULL. Utiliza null=True solo para campos no-string, como enteros, booleanos y fechas. En los dos casos, tambin necesitars establecer blank=True si deseas permitir valores vacos en los formularios, ya que el parmetro null solo afecta el almacenamiento en la base de datos (ver la siguiente seccin, titulada blank). Evita utilizar null en campos basados en string como CharField y TextField salvo que tengas una excelente razn para hacerlo. Si un campo basado en string tiene null=True, eso signica que tiene dos valores posibles para sin datos: NULL y el string vaco. En la mayora de los casos, esto es redundante; la convencin de Django es usar el string vaco, no NULL.
282
25.2.2 blank
Si est en True, est permitido que el campo est en blanco. El valor por omisin es False. Observar que esto es diferente de null. null solo se relaciona con la base de datos, mientras que blank est relacionado con la validacin. Si un campo tiene blank=True, la validacin del administrador de Django permitir la entrada de un valor vaco. Si un campo tiene blank=False, es un campo requerido.
25.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 administracin de Django utilizar un cuadro de seleccin en lugar del campo de texto estndar, 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 opcin. La lista de opciones puede ser denida tambin 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)
Para cada campo del modelo que tenga establecidas choices, Django agregar un mtodo para recuperar el nombre legible por humanos para el valor actual del campo. Ver Apndice C para ms detalles.
25.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 ests 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 estn permitidos en un nombre de variable de Python (en particular el guin simple), no hay problema. Django quotea los nombres de columna y tabla detrs de la escena.
283
25.2.5 db_index
Si est en True, Django crear un ndice en la base de datos para esta columna cuando cree la tabla (es decir, cuando ejecute manage.py syncdb).
25.2.6 default
El valor por omisin del campo.
25.2.7 editable
Si es False, el campo no ser editable en la interfaz de administracin o via procesamiento de formularios. El valor por omisin es True.
25.2.8 help_text
Texto de ayuda extra a ser mostrado bajo el campo en el formulario de administracin del objeto. Es til como documentacin aunque tu objeto no tenga formulario de administracin.
25.2.9 primary_key
Si es True, este campo es la clave primaria del modelo. Su no se especica primary_key=True para ningn campo del modelo, Django agregar automticamente este campo:
id = models.AutoField( ID , primary_key=True)
Por lo tanto, no necesitas establecer primary_key=True en ningn campo, salvo que quieras sobreescribir el comportamiento por omisin de la clave primaria. primary_key=True implica blank=False, null=False, y unique=True. Solo se permite una clave primaria en un objeto.
25.2.10 radio_admin
Por omisin,el administrador de Django usa una interfaz de cuadro de seleccin (<select>) para campos que son ForeignKey o tienen choices. Si radio_admin es True, Django utilizar una interfaz radiobutton en su lugar. No utilice esto para un campo que no sea ForeignKey o no tenga choices.
25.2.11 unique
Si es True, el valor para este campo debe ser nico en la tabla.
25.2.12 unique_for_date
Asignar como valor el nombre de un DateField o DateTimeField para requerir que este campo sea nico para el valor del campo tipo fecha, por ejemplo:
class Story(models.Model): pub_date = models.DateTimeField() slug = models.SlugField(unique_for_date= " pub_date " ) ...
284
En este cdigo, Django no permitir la creacin de dos historias con el mismo slug publicados en la misma fecha. Esto diere de usar la restriccin unique_together en que solo toma en cuenta la fecha del campo pub_date; la hora no importa.
25.2.13 unique_for_month
Similar a unique_for_date, pero requiere que el campo sea nico con respecto al mes del campo dado.
25.2.14 unique_for_year
Similar a unique_for_date y unique_for_month, pero para el ao.
25.2.15 verbose_name
Cada tipo de campo, excepto ForeignKey, ManyToManyField, y OneToOneField, toma un primer argumento posicional opcional un nombre descriptivo. Si el nombre descriptivo no est dado, Django lo crear automticamente usando el nombre de atributo del campo, convirtiendo guiones bajos en espacios. En este ejemplo, el nombre descriptivo es "Persons first name":
first_name = models.CharField( " Person s first name " , maxlength=30)
ForeignKey, ManyToManyField, y OneToOneField requieren que el primer argumento sea una clase del modelo, en este caso hay que usar 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 convencin es no capitalizar la primera letra del verbose_name. Django convertir a mayscula automticamente la primera letra cuando lo necesite.
25.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 ms comunes en las bases de datos: muchos-a-uno, muchos-amuchos, y uno-a-uno. Sin embargo, la semntica de las relaciones uno-a-uno esta siendo revisada mientras se imprime este libro, as que no se cubren en esta seccin. Consulte en la documentacin on-line la informacin ms actualizada.
25.3. Relaciones
285
Por ejemplo, si un modelo Car tiene un Manufacturer es decir, un Manufacturer fabrica mltiples autos pero cada Car tiene solo un Manufacturer usa la siguiente denicin:
class Manufacturer(models.Model): ... class Car(models.Model): manufacturer = models.ForeignKey(Manufacturer) ...
Para crear una relacin recursiva un objeto que tiene una relacin muchos-a-uno con l mismo usa models.ForeignKey(self):
class Employee(models.Model): manager = models.ForeignKey( self )
Si necesitas crear una relacin con un modelo que an no se ha denido, puedes usar el nombre del modelo en lugar del objeto modelo:
class Car(models.Model): manufacturer = models.ForeignKey( Manufacturer ) ... class Manufacturer(models.Model): ...
Observar que 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 aplicacin diferente, o hacer referencia a un modelo que ha sido importado de cualquier otro lado. Detrs de la escena, Django agrega "_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 Car, tendr una columna manufacturer_id. (Puedes cambiar esto explcitamente especicando db_column; ver ms arriba en la seccin db_column.) De todas formas, tu cdigo nunca debe utilizar el nombre de la columna de la base de datos, salvo que escribas tus propias SQL. Siempre te manejars 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 minsculas. 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 relacin (ver Tabla B-5). Todos son opcionales.
286
Argumento edit_inline
limit_choices_to
max_num_in_admin
min_num_in_admin
num_extra_on_change
num_in_admin
raw_id_admin
related_name
Descripcin Si no es False, este objeto relacionado se edita inline en la pgina del objeto relacionado. Esto signica que el objeto no tendr su propia interfaz de administracin. Usa models.TABULAR o models.STACKED, que designan si los objetos editables inline se muestran como una tabla o como una pila de conjuntos de campos, respectivamente. Un diccionario para buscar argumentos y valores (ver el Apndice C) que limita las opciones de administracin disponibles para este objeto. Usa esto con funciones del mdulo datetime de Python para limitar las opciones de fecha de los objetos. Por ejemplo: limit_choices_to = { pub_date__lte : datetime. slo permite la eleccin de objetos relacionados con pub_date anterior a la fecha/hora actual. En lugar de un diccionario, esto puede ser un objeto Q (ver Apndice C) para consultas ms complejas. No es compatible con edit_inline. Para objetos editados inline, este es el nmero mximo de objetos relacionados a mostrar en la interfaz de administracin. Por lo tanto, si una pizza puede tener como mximo diez ingredientes, max_num_in_admin=10 asegurar que un usuario nunca ingresar ms de diez ingredientes. Observar que esto no asegura que no se puedan crear ms de diez ingredientes relacionados. Simplemente controla la interfaz de administracin; no fortalece cosas a nivel de Python API o base de datos. La cantidad mnima de objetos relacionados que se muestran en la interfaz de administracin. Normalmente, en el momento de la creacin se muestran num_in_admin objetos inline , y en el momento de edicin se muestran num_extra_on_change objetos en blanco adems de todos los objetos relacionados preexistentes. De todas formas, nunca se mostrarn menos de min_num_in_admin objetos relacionados. La cantidad de campos en blanco extra de objetos relacionados a mostrar en el momento de realizar cambios. El valor por omisin de la cantidad de objetos inline a mostrar en la pgina del objeto en el momento de agregar. 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 prctico utilizar una caja de seleccin. No es utilizado con edit_inline. El nombre a utilizar para la relacin desde el objeto relacionado de hacia ste objeto. Para ms informacin, ver el Apndice C. El campo en el objeto relacionado con el cual se establece la relacin. Por omisin, Django usa287 la clave primaria del objeto relacionado.
Como sucede con ForeignKey, una relacin 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 todava 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 aplicacin 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 trmino en plural que describa al conjunto de objetos relacionados con el modelo. Detrs de la escena, Django crea una tabla join intermedia para representar la relacin muchos-amuchos. No importa cual de los modelos tiene el ManyToManyField, pero es necesario que est en uno de los modelos no en los dos. Si ests usando la interfaz de administracin, las instancias ManyToManyField deben ir en el objeto que va a ser editado en la interfaz de administracin. En el ejemplo anterior, los toppings estn en la Pizza (en lugar de que el Topping tenga pizzas ManyToManyField ) porque es ms 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 administracin de laPizza permitir que los usuarios selecciones los ingredientes. Los objetos ManyToManyField toman algunos argumentos extra para denir como debe trabajar la relacin (ver Tabla B-6). Todos son opcionales. ArgumenDescripcin to related_namel nombre a utilizar para la relacin desde el objeto relacionado hacia este objeto. Ver E Apndice C para ms informacin. filter_interface interfaz de ltro JavaScript agradable y discreta en lugar de la menos Usa una cmoda <select multiple> en el formulario administrativo de este objeto. El valos debe ser models.HORIZONTAL o models.VERTICAL (es decir, la interfaz debe apilarse horizontal o verticalmente). limit_choices_to Ver la descripcin en ForeignKey. symmetricalSolo utilizado en la denicin de ManyToManyField sobre s mismo. Considera el siguiente modelo: class Person(models.Model): friends = models.ManyToManyField(self) Cuando Django procesa este modelo, identica que tiene un ManyToManyField sobre s mismo, y como resultado, no agrega un atributo person_set a la clase Person. En lugar de eso, se asumen que el ManyToManyField es simtrico esto es, si yo soy tu amigo, entonces tu eres mi amigo. Si no deseas la simetra en las relaciones ManyToMany con self, establece symmetrical en False. Esto forzar a Django a agregar el descriptor para la relacin inversa, permitiendo que las relaciones ManyToMany sean asimtricas. db_table El nombre de la tabla a crear para almacenar los datos de la relacin muchos-a-muchos. Si no se provee, Django asumir un nombre por omisin basado en los nombres de las dos tablas a ser vinculadas. 288 Captulo 25. Apndice B: Referencia de la denicin de modelos
Los metadatos del modelo son cualquier cosa que no sea un campo, como opciones de ordenamiento, etc. Las secciones que siguen presentan una lista de todas las posibles Meta opciones. Ninguna de estas opciones es requerida. Agregar class Meta a un modelo es completamente opcional.
25.4.1 db_table
El nombre de la tabla de la base de datos a usar para el modelo. Para ahorrarte tiempo, Django deriva automticamente el nombre de la tabla de la base de datos a partir del nombre de la clase modelo y la aplicacin que la contiene. Un nombre de tabla de base de datos de un modelo se construye uniendo la etiqueta de la aplicacin del modelo el nombre que usaste en manage.py startapp con el nombre de la clase modelo, con un guin bajo entre ellos. Por ejemplo, si tienes una aplicacin books (creada por manage.py startapp books), un modelo denido como class Book tendr una tabla en la base de datos llamada books. Para sobreescribir el nombre de la tabla de la base de datos, use el parmetro db_table dentro de class Meta:
class Book(models.Model): ... class Meta: db_table = things_to_read
Si no se dene, Django utilizar app_label + _ + model_class_name. Ver la seccin Nombres de Tabla_ para ms informacin. Si tu nombre de tabla de base de datos es una palabra reservada de SQL, o contiene caracteres que no estn permitidos en los nombres de variable de Python (especialmente el guin simple), no hay problema. Django quotea los nombres de tabla y de columna detrs de la escena.
25.4.2 get_latest_by
El nombre de un DateField o DateTimeField del modelo. Esto especica el campo a utilizar por omisin en el mtodo latest() del Manager del modelo. Aqu hay un ejemplo:
class CustomerOrder(models.Model): order_date = models.DateTimeField() ... class Meta: get_latest_by = " order_date "
289
25.4.3 order_with_respect_to
Marca este objeto como ordenable con respecto al campo dado. Esto se utiliza casi siempre con objetos relacionados para permitir que puedan ser ordenados respecto a un objeto padre. Por ejemplo, si una Answer se relaciona a un objeto Question, y una pregunta tiene ms de una respuesta, y el orden de las respuestas importa, hars esto:
class Answer(models.Model): question = models.ForeignKey(Question) # ... class Meta: order_with_respect_to = question
25.4.4 ordering
El ordenamiento por omisin del objeto, utilizado cuando se obtienen listas de objetos:
class Book(models.Model): title = models.CharField(maxlength=100) class Meta: ordering = [ title ]
Esto es una tupla o lista de strings. Cada string es un nombre de campo con un prejo opcional -, que indica orden descendiente. Los campos sin un - precedente se ordenarn en forma ascendente. Use el string " para ordenar al azar. Por ejemplo, para ordenar por un campo title en orden ascendente (A-Z), usa esto:
ordering = [ title ]
Para ordenar por title en orden descendente, y luego por author en orden ascendente, usa esto:
ordering = [ -title , author ]
Observar que no importa cuantos campos haya en ordering, el sitio de administracin usa slo el primer campo.
25.4.5 permissions
Permisos extra para almacenar en la tabla de permisos cuando se crea este objeto. Se crean automticamente permisos para agregar, eliminar y cambiar para cada objeto que tenga establecida la opcin admin. Este ejemplo especica un permiso extra, can_deliver_pizzas:
class Employee(models.Model): ... class Meta: permissions = ( ( " can_deliver_pizzas " , " Can deliver pizzas " ), )
tuplas
de
la
forma
(permission_code,
Ver el Captulo 12 para ms detalles sobre permisos. 290 Captulo 25. Apndice B: Referencia de la denicin de modelos
25.4.6 unique_together
Conjuntos de nombres de campo que tomados juntos deben ser nicos:
class Employee(models.Model): department = models.ForeignKey(Department) extension = models.CharField(maxlength=10) ... class Meta: unique_together = [( " department " , " extension " )]
Esto es una lista de listas de campos que deben ser nicos cuando se consideran juntos. Es usado en la interfaz de administracin de Django y se refuerza a nivel de base de datos (esto es, se incluyen las sentencias UNIQUE apropiadas en la sentencia CREATE TABLE).
25.4.7 verbose_name
Un nombre legible por humanos para el objeto, en singular:
class CustomerOrder(models.Model): order_date = models.DateTimeField() ... class Meta: verbose_name = " order "
Si no se dene, Django utilizar una versin adaptada del nombre de la clase, en la cual CamelCase se convierte en camel case.
25.4.8 verbose_name_plural
El nombre del objeto en plural:
class Sphynx(models.Model): ... class Meta: verbose_name_plural = " sphynges "
25.5 Managers
Un Manager es la interfaz a travs de la cual se proveen las operaciones de consulta de la base de datos a los modelos de Django. Existe al menos un Manager para cada modelo en una aplicacin Django. La forma en que trabajan las clases Manager est documentada en el Apndice C. Esta seccin trata especcamente las opciones del modelo que personaliza el comportamiento del Manager.
25.5. Managers
291
Usando este modelo de ejemplo, Person.objects generar una excepcin AttributeError (dado que Person no tiene un atributo objects), pero Person.people.all() devolver una lista de todos los objetos Person.
292
En este ejemplo, puedes usar OpinionPoll.objects.with_counts() para retornar la lista de objetos OpinionPoll con el atributo num_responses. Otra cosa a observar en este ejemplo es que los mtodos de un Manager pueden acceder a self.model para obtener la clase modelo a la cual estn anexados. Modicando los QuerySets iniciales del Manager Un QuerySet base de un Manager devuelve todos los objetos en el sistema. Por ejemplo, usando este modelo:
class Book(models.Model): title = models.CharField(maxlength=100) author = models.CharField(maxlength=50)
la sentencia Book.objects.all() retornar todos los libros de la base de datos. Puedes sobreescribir el QuerySet base, sobreescribiendo el mtodo Manager.get_query_set(). get_query_set() debe retornar un QuerySet con las propiedades que tu requieres. Por ejemplo, el siguiente modelo tiene dos managers uno que devuelve todos los objetos, y otro que retorna solo los libros de Roald Dahl:
# First, define the Manager subclass. class DahlBookManager(models.Manager): def get_query_set(self): return super(DahlBookManager, self).get_query_set().filter(author= Roald Dahl ) # Then hook it into the Book model explicitly. class Book(models.Model): title = models.CharField(maxlength=100) author = models.CharField(maxlength=50) objects = models.Manager() # The default manager. dahl_objects = DahlBookManager() # The Dahl-specific manager.
Con este modelo de ejemplo, Book.objects.all() retornar todos los libros de la base de datos, pero Book.dahl_objects.all() solo retornar aquellos escritos por Roald Dahl. Por supuesto, como get_query_set() devuelve un objeto QuerySet, puedes usar filter(), exclude(), y todos los otro mtodos de QuerySet sobre l. Por lo tanto, estas sentencias son todas legales:
Book.dahl_objects.all() Book.dahl_objects.filter(title= Matilda ) Book.dahl_objects.count()
Este ejemplo tambin seala otra tcnica interesante: usar varios managers en el mismo modelo. Puedes agregar tantas instancias de Manager() como quieras. Esta es una manera fcil de denir lters comunes para tus modelos. Aqu hay un ejemplo:
class MaleManager(models.Manager): def get_query_set(self): return super(MaleManager, self).get_query_set().filter(sex= M ) class FemaleManager(models.Manager): def get_query_set(self): return super(FemaleManager, self).get_query_set().filter(sex= F ) class Person(models.Model):
25.5. Managers
293
first_name = models.CharField(maxlength=50) last_name = models.CharField(maxlength=50) sex = models.CharField(maxlength=1, choices=(( M , Male ), ( F , Female ))) people = models.Manager() men = MaleManager() women = FemaleManager()
Este ejemplo te permite consultar Person.men.all(), Person.people.all(), con los resultados predecibles.
Person.women.all(),
Si usas objetos Manager personalizados, toma nota que el primer Manager que encuentre Django (en el orden en el que estn denidos en el modelo) tiene un status especial. Django interpreta el primer Manager denido en una clase como el Manager por omisin. Ciertas operaciones como las del sitio de administracin de Django usan el Manager por omisin para obtener listas de objetos, por lo que generalmente es una buena idea que el primer Manager est relativamente sin ltrar. En el ltimo ejemplo, el manager people est denido primero por lo cual es el Manager por omisin.
El ltimo mtodo en este ejemplo es una propiedad un atributo implementado por cdigo getter/setter personalizado. Las propiedades son un truco ingenioso agregado en Python 2.2; puedes leer ms acerca de ellas en http://www.python.org/download/releases/2.2/descrintro/#property. Existen tambin un puado de mtodos de modelo que tienen un signicado especial para Python o Django. Estos mtodos se describen en las secciones que siguen.
294
25.6.1 __str__
__str__() es un mtodo mgico de Python que dene lo que debe ser devuelto si llamas a str() sobre el objeto. Django usa str(obj) (o la funcin relacionada unicode(obj), que se describe ms abajo) en varios lugares, particularmente como el valor mostrado para hacer el render de un objeto en el sitio de administracin de Django y como el valor insertado en un plantilla cuando muestra un objeto. Por eso, siempre debes retornar un string agradable y legible por humanos en el __str__ de un objeto. A pesar de que esto no es requerido, es altamente recomendado. Aqu hay un ejemplo:
class Person(models.Model): first_name = models.CharField(maxlength=50) last_name = models.CharField(maxlength=50) def __str__(self): return %s %s % (self.first_name, self.last_name)
25.6.2 get_absolute_url
Dene un mtodo get_absolute_url() para decirle a Django cmo calcular la URL de un objeto, por ejemplo:
def get_absolute_url(self): return " /people/ %i / " % self.id
Django usa esto en la interfaz de administracin. Si un objeto dene get_absolute_url(), la pgina de edicin del objeto tendr un enlace View on site, que te llevar directamente a la vista pblica del objeto, segn get_absolute_url(). Tambin un par de otras partes de Django, como el framework de sindicacin de feeds, usan get_absolute_url() como facilidad para recompensar a las personas que han denido el mtodo. Es una buena prctica usar get_absolute_url() en plantillas, en lugar de codicar en duro las URL de tus objetos. Por ejemplo, este cdigo de plantilla es malo:
<a href="/people/{{ object.id }}/">{{ object.name }}</a>
El problema con la forma en que simplemente escribimos get_absolute_url() es que viola levemente el principio DRY: la URL de este objeto de dene dos veces, en el archivo URLconf y en el modelo. Adems puedes desacoplar tus modelos de el URLconf usando el decorator permalink. A este decorator se le pasa funcin de view, una lista de parmetros posicionales, y (opcionalmente) un diccionario de parmetros por nombre. Django calcula la URL completa correspondiente usando el URLconf, sustituyendo los parmetros que le has pasado en la URL. Por ejemplo, si tu URLconf contiene una lnea como sta:
( r ^people/( \ d+)/$ , people.views.details ),
295
Observar que especicamos una secuencia vaca para el segundo argumento en este caso, porque slo queremos pasar argumentos por clave, no argumentos por nombre. De esta forma, ests ests ligando la URL absoluta del modelo a la vista que se utiliza para mostrarla, sin repetir la informacin de la URL en ningn lado. An puedes usar el mtodo get_absolute_url en plantillas, como antes.
%s " , [self.baz])
connection y cursor implementan en su mayor parte la DB-API estndar de Python (http://www.python.org/peps/pep-0249.html). Si no ests familiarizado con la DB-API de Python, observa que la sentencia SQL en cursor.execute() usa placeholders, " %s", en lugar de agregar los parmetros directamente dentro de la SQL. Si usas esta tcnica, la biblioteca subyacente de base de datos automticamente agregar comillas y secuencias de escape a tus parmetros segn sea necesario. (Observar tambin que Django espera el placeholder " %s", no el placeholder ", que es utilizado por los enlaces Python a SQLite. Python bindings. Esto es por consistencia y salud mental). Una nota nal: Si todo lo que quieres hacer es usar una clusula WHERE personalizada, puedes usar los argumentos where, tables, y params de la API estndar de bsqueda. Ver Apndice C.
296
25.7.1 date_hierarchy
Establece date_hierarchy con el nombre de un DateField o DateTimeField en tu modelo, y la pgina de la lista de cambios incluir una navegacin basada en la fecha usando ese campo. Aqu hay un ejemplo:
class CustomerOrder(models.Model): order_date = models.DateTimeField() ... class Admin: date_hierarchy = " order_date "
25.7.2 elds
Establece fields para controlar la disposicin de las pginas agregar y modicar de la interfaz de administracin. fields es una estructura anidada bastante compleja que se demuestra mejor con un ejemplo. Lo siguiente est tomado del modelo FlatPage que es parte de django.contrib.flatpages:
class FlatPage(models.Model): ... class Admin: fields = ( (None, { fields : ( url , title , content , sites ) }), ( Advanced options , {
297
Formalmente, fields es una lista de tuplas dobles, en la que cada tupla doble representa un <fieldset> en el formulario de la pgina de administracin. (Un <fieldset> es una seccin del formulario.) Las tuplas dobles son de la forma (name, field_options), donde name es un string que representa el ttulo del conjunto de campos, y field_options es un diccionario de informacin acerca del conjunto de campos, incluyendo una lista de los campos a mostrar en l. Si fields no est denido, Django mostrar por omisin cada campo que no sea un AutoField y tenga editable=True, en un conjunto de campos simple, en el mismo orden en que aparecen los campos denidos en el modelo. El diccionario field_options puede tener las clave que se describen en la siguiente seccin. elds Una tupla de nombres de campo a mostrar en el conjunto de campos. Esta clave es requerida. Para mostrar mltiples campos en la misma linea, encierra esos campos en su propia tupla. En este ejemplo, los campos first_name y last_name se mostrarn en la misma lnea:
fields: ((first_name, last_name), address, city, state),
classes Un string conteniendo clases extra CSS para aplicar al conjunto de campos. Aplica mltiples clases separndolas con espacios:
classes: wide extrapretty,
Dos clases tiles denidas por la hoja de estilo del sitio de administracin por omisin son collapse y wide. Los conjuntos de campos con el estilo collapse sern colapsados inicialmente en el sitio de administracin y reemplazados por un pequeo enlace click to expand. Los conjuntos de campos con el estilo wide tendrn espacio horizontal extra. description Un string de texto extra opcional para mostrar encima de cada conjunto de campos, bajo el encabezado del mismo. Se usa tal cual es, de manera que puedes usar cualquier HTML, y debes crear las secuencias de escape correspondientes para cualquier carcter especial HTML (como los ampersands).
25.7.3 js
Una lista de strings representando URLs de archivos JavaScript a vincular en la pantalla de administracin mediante etiquetas <script src=>. Esto puede ser usado para ajustar un tipo determinado de pgina de administracin en JavaScript o para proveer quick links para llenar valores por omisin para ciertos campos. Si usas URLs relativas esto es, URLs que no empiezan con http:// o / entonces el sitio de administracin prejar automticamente estos enlaces con settings.ADMIN_MEDIA_PREFIX.
298
25.7.4 list_display
Establece list_display para controlar que campos se muestran en la pgina de la lista de del administrador. Si no se dene list_display, el sitio de administracin mostrar una columna simple con la representacin __str__() de cada objeto. Aqu hay algunos casos especiales a obsevar acerca de list_display: Si el campo es una ForeignKey, Django mostrar el __str__() del objeto relacionado. No se admiten los campos ManyToManyField, porque eso implicara la ejecucin de una sentencia SQL separada para cada la en la tabla. Si de todas formas quieres hacer esto, dale a tu modelo un mtodo personalizado, y agrega el nombre de ese mtodo a list_display. (Ms informacin sobre mtodos personalizados en list_display en breve.) Si el campo es un BooleanField o NullBooleanField, Django mostrar unos bonitos iconos on o off en lugar de True o False. Si el string dado es un mtodo del modelo, Django lo invocar y mostrar la salida. Este mtodo debe tener un atributo de funcin short_description para usar como encabezado del campo. Aqu est un modelo de ejemplo completo:
class Person(models.Model): name = models.CharField(maxlength=50) birthday = models.DateField() class Admin: list_display = (name, decade_born_in) def decade_born_in(self): return self.birthday.strftime( %Y)[:3] + "0s" decade_born_in.short_descripcin = Birth decade
Si el string dado es un mtodo del modelo, Django har un HTML-escape de la salida por omisin. Si no quieres escapear la salida del mtodo, dale al mtodo un atributo allow_tags con el valor en True. Aqu est un modelo de ejemplo completo:
class Person(models.Model): first_name = models.CharField(maxlength=50) last_name = models.CharField(maxlength=50) color_code = models.CharField(maxlength=6) class Admin: list_display = ( first_name , last_name , colored_name ) def colored_name(self): return <span style= " color: # %s ; " > %s colored_name.allow_tags = True
Si el string dado es un mtodo del modelo que retorna True o False, Django mostrar un bonito icono on o off si le das al mtodo un atributo boolean con valor en True. Aqu est un modelo de ejemplo completo:
class Person(models.Model): first_name = models.CharField(maxlength=50) birthday = models.DateField() class Admin: list_display = ( name , born_in_fifties )
299
Los mtodos __str__() son tan vlidos en list_display como cualquieras otro mtodo del modelo, por lo cual est perfectamente OK hacer esto:
list_display = ( __str__ , some_other_field )
Usualmente, 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). De todas formas, si un elemento de list_display representa cierto campo de la base de datos, puedes indicar este hecho estableciendo el atributo admin_order_field del tem, por ejemplo:
class Person(models.Model): first_name = models.CharField(maxlength=50) color_code = models.CharField(maxlength=6) class Admin: list_display = ( first_name , colored_first_name )
def colored_first_name(self): return <span style= " color: # %s ; " > %s </span> % (self.color_code, self.first_na colored_first_name.allow_tags = True colored_first_name.admin_order_field = first_name
El cdigo precedente le dir a Django que ordene segn el campo first_name cuando trate de ordenar por colored_first_name en el sitio de administracin.
25.7.5 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. Por omisin, la pgina de la lista de cambios vincular la primera columna el primer campo especicado en list_display a la pgina de cambios de cada tem. Pero list_display_links te permite cambiar cules columnas se vinculan. Establece list_display_links a una lista o tupla de nombres de campo (en el mismo formato que list_display) para vincularlos. list_display_links puede especicar uno o varios nombres de campo. Mientras los nombres de campo aparezcan en list_display, a Django no le preocupa si los campos vinculados son muchos o pocos. El nico requerimiento es que si quieres usarlist_display_links, debes denir list_display. En este ejemplo, los campos first_name y last_name sern vinculados a la pgina de la lista de cambios:
class Person(models.Model): ... class Admin: list_display = ( first_name , last_name , birthday ) list_display_links = ( first_name , last_name )
Finalmente, observa que para usar list_display_links, debes denir tambin list_display.
300
25.7.6 list_lter
Establece list_filter para activar los ltros en la barra lateral derecha de la pgina de la lista de cambios en la interfaz de administracin. Debe ser una lista de nombres de campo, y cada campo especicado debe ser de alguno de los tipos BooleanField, DateField, DateTimeField, o ForeignKey. Este ejemplo, tomado del modelo django.contrib.auth.models.User muestra como trabajan ambos, list_display y list_filter:
class User(models.Model): ...
class Admin: list_display = ( username , email , first_name , last_name , is_staff list_filter = ( is_staff , is_superuser )
25.7.7 list_per_page
Establece list_per_page para controlar cuantos items aparecen en cada pgina de la lista de cambios del administrador. Por omisin, este valor se establece en 100.
25.7.8 list_select_related
Establece list_select_related para indicarle a Django que use select_related() al recuperar la lista de objetos de la pgina de la lista de cambios del administrador. Esto puede ahorrarte una cantidad de consultas a la base de datos si ests utilizando objetos relacionados en la lista de cambios que muestra el administrador. El valor debe ser True o False. Por omisin es False, salvo que uno de los campos list_display sea una ForeignKey. Para ms detalles sobre select_related(), ver Apndice C.
25.7.9 ordering
Establece ordering para especicar como deben ordenarse los objetos en la pgina de la lista de cambios del administrador. Esto debe ser una lista o tupla en el mismo formato que el parmetro ordering del modelo. Si no est denido, la interfaz de administracin de Django usar el ordenamiento por omisin del modelo.
25.7.10 save_as
Establece save_as a True para habilitar la caracterstica save as en los formularios de cambios del administrador. Normalmente, los objetos tienen tres opciones al guardar: Save, Save and continue editing y Save and add another. Si save_as es True, Save and add another ser reemplazado por un botn Save as. Save as signica que el objeto ser guardado como un objeto nuevo (con un identicador nuevo), en lugar del objeto viejo. Por omisin, save_as es False.
301
25.7.11 save_on_top
Establece save_on_top para agregar botones de guardado a lo largo del encabezado de tus formularios de cambios del administrador. Normalmente, los botones de guardado aparecen solamente al pie de los formularios. Si estableces save_on_top, los botones aparecern en el encabezado y al pi del formulario. Por omisin, save_on_top es False.
25.7.12 search_elds
Establece search_fields para habilitar un cuadro de bsqueda en la pgina de la lista de cambios del administrador. Debe ser una lista de nombres de campo que se utilizar para la bsqueda cuando alguien enve una consulta en ese cuadro de texto. Estos campos deben ser de alguna tipo de campo de texto, como CharField o TextField. Tambin puedes realizar una bsqueda relacionada sobre una ForeignKey con la notacin de bsqueda de la API:
class Employee(models.Model): department = models.ForeignKey(Department) ... class Admin: search_fields = [ department__name ]
Cuando alguien hace una bsqueda en el cuadro de bsqueda del administrador, Django divide la consulta de bsqueda en palabras y retorna todos los objetos que contengan alguna de las palabras, sin distinguir maysculas y minsculas, donde cada palabra debe estar en al menos uno de los search_fields. Por ejemplo, si search_fields es [first_name, last_name] y un usuario busca john lennon, Django har el equivalente a esta clusula WHERE en SQL:
WHERE (first_name ILIKE %john % OR last_name ILIKE %john %) AND (first_name ILIKE %lennon % OR last_name ILIKE %lennon %)
Para bsquedas ms rpidas y/o ms restrictivas, agrega como prejo al nombre de campo un operador como se muestra en la Tabla B-7. erador ^ Signicado Matchea el principio del campo. Por ejemplo, si search_fields es [^first_name, ^last_name], y un usuario busca john lennon, Django har el equivalente a esta clusula WHERE en SQL: WHERE (rst_name ILIKE john % OR last_name ILIKE john %) AND (rst_name ILIKE lennon % OR last_name ILIKE lennon %) Esta consulta es ms eciente que la consulta %john %, dado que la base de datos solo necesita examinar el principio de una columna de datos, en lugar de buscar a travs de todos los datos de la columna. Adems, si la columna tiene un ndice, algunas bases de datos pueden permitir el uso del ndice para esta consulta, a pesar de que sea una consulta LIKE. Matchea exactamente, sin distinguir maysculas y minsculas. Por ejemplo, si search_fields es [=first_name, =last_name] y un usuario busca john lennon, Django har el equivalente a esta clausula WHERE en SQL: WHERE (rst_name ILIKE john OR last_name ILIKE john) AND (rst_name ILIKE lennon OR last_name ILIKE lennon) Observar que la entrada de la consulta se divide por los espacios, por lo cual actualmente no es posible hacer una bsqueda de todos los registros en los cuales first_name es exactamente john winston (con un espacio en el medio). Realiza una bsqueda en todo el texto. Es similar al mtodo de bsqueda predeterminado, pero usa un ndice. Actualmente solo est disponible para MySQL.
302
CAPTULO 26
La API de base de datos de Django es la otra mitad de la API de modelos discutido en el Apndice B. Una vez que hayas denido un modelo, usars esta API en cualquier momento que necesites acceder a la base de datos. Has visto ejemplos del uso de esta API a travs del libro; este apndice explica todas las varias opciones detalladamente. De manera similar a lo que ocurre con las APIs de modelos descriptos en el apndice B, estas APIs son considerados muy estables, aunque los desarrolladores de Django constantemente aaden nuevos atajos y conveniencias. Es buena idea consultar siempre la documentacin en lnea ms actual que est disponible en http://www.djangoproject.com/documentation/. A lo largo de este apndice, vamos a hacer referencia a los siguientes modelos, los cuales pueden formar una simple aplicacin de blog:
from django.db import models class Blog(models.Model): name = models.CharField(max_length=100) tagline = models.TextField() def __unicode__(self): return self.name class Author(models.Model): name = models.CharField(max_length=50) email = models.EmailField() def __unicode__(self): return self.name class Entry(models.Model): blog = models.ForeignKey(Blog) headline = models.CharField(max_length=255) body_text = models.TextField() pub_date = models.DateTimeField() authors = models.ManyToManyField(Author) def __unicode__(self): return self.headline
303
Esto, detrs de escena, ejecuta una sentencia SQL INSERT. Django no accede a la base de datos hasta que t explcitamente invoques a save(). El mtodo save() no retorna nada. Para crear un objeto y grabarlo todo en un paso revisa el mtodo create de la clase Manager que describiremos en breve.
304
No hay forma de saber cual ser el valor de un identicador antes que llames a save() esto se debe a que ese valor es calculado por tu base de datos, no por Django. Si un modelo tiene un AutoField pero quieres denir el identicador de un nuevo objeto explcitamente cuando grabas, solo defnelo explcitamente antes de grabarlo en vez de conar en la asignacin automtica de valor del identicador:
>>> >>> 3 >>> >>> 3 b3 = Blog(id=3, name= Cheddar Talk , tagline= Thoughts on cheese. ) b3.id b3.save() b3.id
Si asignas manualmente valores de claves primarias autoincrementales Asegrate de no usar un valor de clave primaria que ya existe!. Si creas un objeto con un valor explcito de clave primaria que ya existe en la base de datos, Django asumir que ests cambiando el registro existente en vez de crear uno nuevo. Dado el ejemplo precedente de blog Cheddar Talk, este ejemplo sobrescribira el registro previo en la base de datos:
>>> b4 = Blog(id=3, name= Not Cheddar , tagline= Anything but cheese. ) >>> b4.save() # Overrides the previous blog with ID=3!
El especicar explcitamente valores de claves primarias autoincrementales es ms til cuando se estn grabando objetos en lotes, cuando ests seguro de que no tendrs colisiones de claves primarias.
Detrs de escena, esto ejecuta una sentencia SQL UPDATE. De nuevo: Django no accede a la base de datos hasta que llamas explcitamente a save(). Como sabe Django cuando usar UPDATE y cuando usar INSERT Habrs notado que los objetos de base de datos de Django usan el mismo mtodo save() para crear y cambiar objetos. Django abstrae la necesidad de usar sentencias SQL INSERT o UPDATE. Especcamente, cuando llamas a save(), Django sigue este algoritmo: Si el atributo clave primaria del objeto tiene asignado un valor que evala True (esto es, un valor distinto a None o a la cadena vaca) Django ejecuta una consulta SELECT para determinar si existe un registro con la clave primaria especicada. Si el registro con la clave primaria especicada ya existe, Django ejecuta una consulta UPDATE. Si el atributo clave primaria del objeto no tiene valor o si lo tiene pero no existe un registro, Django ejecuta un INSERT.
305
Debido a esto, debes tener cuidado de no especicar un valor explcito 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. La actualizacin de campos ForeignKey funciona exactamente de la misma forma; simplemente asigna un objeto del tipo correcto al campo en cuestin:
>>> joe = Author.objects.create(name= " Joe " ) >>> entry.author = joe >>> entry.save()
Hay bastantes partes mviles detrs de escena aqu: cuando recuperas objetos de la base de datos, ests construyendo realmente un QuerySet usando el Manager del modelo. Este QuerySet sabe como ejecutar SQL y retornar los objetos solicitados. El Apndice B trat ambos objetos desde el punto de vista de la denicin del modelo; ahora vamos a ver cmo funcionan. Un QuerySet representa una coleccin de objetos de tu base de datos. Puede tener cero, uno, o muchos ltros criterios que limitan la coleccin basados en parmetros provistos. En trminos de SQL un QuerySet se compara a una declaracin SELECT y un ltro es una clusula de limitacin como por ejemplo WHERE o LIMIT. Consigues un QuerySet usando el Manager del modelo. Cada modelo tiene por lo menos un Manager y tiene, por omisin, el nombre objects. Accede al mismo directamente a travs de la clase del modelo, as:
>>> Blog.objects <django.db.models.manager.Manager object at 0x137d00d>
Los Managers solo son accesibles a travs de las clases de los modelos, en vez desde una instancia de un modelo, para as hacer cumplir con la separacin entre las operaciones a nivel de tabla y las operaciones a nivel de registro:
>>> b = Blog(name= Foo , tagline= Bar ) >>> b.objects Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: Manager isnt accessible via Blog instances.
El Manager es la principal fuente de QuerySets para un modelo. Acta como un QuerySet raz que describe todos los objetos de la tabla de base de datos del modelo. Por ejemplo, Blog.objects es el QuerySets inicial que contiene todos los objetos Blog en la base de datos.
306
En un QuerySet recin creado, el cache esta vaco. La primera vez que un QuerySet es evaluado y, por lo tanto, ocurre un acceso a la base de datos Django graba el resultado de la consulta en el cache del QuerySet y retorna los resultados que han sido solicitados explcitamente (por ejemplo, el siguiente elemento, si se est iterando sobre el QuerySet). Evaluaciones subsecuentes del QuerySet re-usan los resultados alojados en el cache. Ten presente este comportamiento de caching, porque puede morderte si no usas tus QuerySets correctamente. Por ejemplo, lo siguiente crear dos QuerySets, los evaluar, y los descartar:
print [e.headline for e in Entry.objects.all()] print [e.pub_date for e in Entry.objects.all()]
Eso signica que la consulta sera ejecutada dos veces en la base de datos, duplicando la carga sobre la misma. Tambin existe una posibilidad de que las dos listas pudieran no incluir los mismos registros de la base de datos, porque se podra haber agregado o borrado un Entry durante el pequesimo perodo de tiempo entre ambas peticiones. Para evitar este problema, simplemente graba el QuerySet y re-salo:
queryset = Poll.objects.all() print [p.headline for p in queryset] # Evaluate the query set. print [p.pub_date for p in queryset] # Reuse the cache from the evaluation.
El mtodo all() retorna un QuerySet de todos los objetos de la base de datos. Sin embargo, usualmente solo necesitars seleccionar un subconjunto del conjunto completo de objetos. Para crear tal subconjunto, renas el QuerySet inicial, aadiendo condiciones con ltros. Usualmente hars esto usando los mtodos filter() y/o exclude():
>>> y2006 = Entry.objects.filter(pub_date__year=2006) >>> not2006 = Entry.objects.exclude(pub_date__year=2006)
Tanto filter() como exclude() toman argumentos de patrones de bsqueda, los cuales se discutirn detalladamente en breve.
Esto toma el QuerySet inicial de todas las entradas en la base de datos, agrega un ltro, luego una exclusin, y luego otro ltro. El resultado nal es un QuerySet conteniendo todas las entradas con un ttulo que empieza con What que fueron publicadas entre Enero 1, 2005, y el da actual. Es importante precisar aqu que los QuerySet son perezosos el acto de crear un QuerySet no implica ninguna actividad en la base de datos. De hecho, las tres lneas precedentes no hacen ninguna llamada a la base de datos; puedes enlazar/encadenar ltros todo el da y Django no ejecutar realmente la consulta hasta que el QuerySet sea evaluado. Puedes evaluar un QuerySet en cualquiera de las siguientes formas: 26.5. Filtrando objetos 307
Iterando: Un QuerySet es iterable, y ejecuta su consulta en la base de datos la primera vez que iteras sobre el. Por ejemplo, el siguiente QuerySet no es evaluado hasta que se iterado sobre l en el bucle for:
qs = Entry.objects.filter(pub_date__year=2006) qs = qs.filter(headline__icontains= " bill " ) for e in qs: print e.headline
Esto imprime todos los ttulos desde el 2006 que contienen bill pero genera solo un acceso a la base de datos. Imprimindolo: Un QuerySet es evaluado cuando ejecutas repr() sobre el mismo. Esto es por conveniencia en el interprete interactivo Python, as puedes ver inmediatamente tus resultados cuando usas el API interactivamente. Rebanado: Segn lo explicado en la prxima seccin Limitando QuerySets, un QuerySet puede ser rebanado usando la sintaxis de rebanado de arreglos de Python. Usualmente el rebanar un QuerySet retorna otro QuerySet (no evaluado), pero Django ejecutar la consulta a la base de datos si usas el parmetro step de la sintaxis de rebanado. Convirtiendo a una lista: Puedes forzar la evaluacin de un QuerySet ejecutando list() sobre el mismo, por ejemplo:
>>> entry_list = list(Entry.objects.all())
Sin embargo, quedas advertido de que esto podra signicar un gran impacto en la memoria porque Django cargar cada elemento de la lista en memoria. En cambio, el iterar sobre un QuerySet sacar ventaja de tu base de datos para cargar datos e inicializar objetos solo a medida que vas necesitando los mismos. Los QuerySets ltrados son nicos Cada vez que renas un QuerySet obtienes un nuevo QuerySet que no est de ninguna manera atado al QuerySet anterior. Cada renamiento crea un QuerySet separado y distinto que puede ser almacenado, usado y re-usado:
q1 = Entry.objects.filter(headline__startswith= " What " ) q2 = q1.exclude(pub_date__gte=datetime.now()) q3 = q1.filter(pub_date__gte=datetime.now())
Estos tres QuerySets son separados. El primero es un QuerySet base que contiene todas las entradas que contienen un ttulo que empieza con What. El segundo es un sub-conjunto del primero, con un criterio adicional que excluye los registros cuyo pub_date es mayor que el da de hoy. El tercero es un sub-conjunto del primero, con un criterio adicional que selecciona solo los registros cuyo pub_date es mayor que el da de hoy. El QuerySet inicial (q1) no es afectado por el proceso de renamiento.
Esto retorna las entradas desde la sexta hasta la dcima (OFFSET 5 LIMIT 5):
>>> Entry.objects.all()[5:10]
308
Generalmente, el rebanar un QuerySet retorna un nuevo QuerySet no evala la consulta. Una excepcin es si usas el parmetro step de la sintaxis de rebanado de Python. Por ejemplo, esto realmente ejecutara la consulta con el objetivo de retornar una lista, objeto de por medio de los primeros diez:
>>> Entry.objects.all()[:10:2]
Para recuperar un solo objeto en vez de una lista (por ej. SELECT foo FROM bar LIMIT 1) usa un simple ndice en vez de un rebanado. Por ejemplo, esto retorna el primer Entry en la base de datos, despus de ordenar las entradas alfabticamente por ttulo:
>>> Entry.objects.order_by( headline )[0]
y es equivalente a lo siguiente:
>>> Entry.objects.order_by( headline )[0:1].get()
Nota, sin embargo, que el primero de estos generar IndexError mientras el segundo generar DoesNotExist si ninguno de los objetos coincide con el criterio dado.
Este resultado ser ordenado por pub_date de forma descendente, luego por headline de forma ascendente. El signo negativo en frente de pub_date" indica orden descendiente. Si el - esta ausente se asume un orden ascendente. Para ordenar aleatoriamente, usa ", as:
>>> Entry.objects.order_by( ? )
distinct() Retorna un nuevo QuerySet que usa SELECT DISTINCT en su consulta SQL. Esto elimina las duplicadas en el resultado de la misma.
309
Por omisin, un QuerySet no eliminar las duplicadas. En la prctica esto raramente es un problema porque consultas simples como Blog.objects.all() no introducen la posibilidad de registros duplicados. Sin embargo, si tu consulta abarca mltiples tablas, es posible obtener resultados duplicados cuando un QuerySet es evaluado. Esos son los casos en los que usaras distinct(). values(*campos) Retorna un QuerySet especial que evala a una lista de diccionarios en lugar de objetos instancia de modelo. Cada uno de esos diccionarios representa un objeto, con las las claves en correspondencia con los nombre de los atributos de los objetos modelo:
# This list contains a Blog object. >>> Blog.objects.filter(name__startswith=Beatles) [Beatles Blog] # This list contains a dictionary. >>> Blog.objects.filter(name__startswith=Beatles).values() [{id: 1, name: Beatles Blog, tagline: All the latest Beatles news.}]
values() puede recibir argumentos posicionales opcionales, *campos, los cuales especican los nombres de campos a los cuales debe limitarse el SELECT. Si especicas los campos, cada diccionario contendr solamente las claves/valores de campos para los campos que especiques. Si no especicas los campos, cada diccionario contendr una clave y un valor para todos los campos en la table de base de datos:
>>> Blog.objects.values() [{id: 1, name: Beatles Blog, tagline: All the latest Beatles news.}], >>> Blog.objects.values( id , name ) [{id: 1, name: Beatles Blog}]
Este mtodo es til cuando sabes de antemano que solo vas a necesitar valores de un pequeo nmero de los campos disponibles y no necesitars la funcionalidad de un objeto instancia de modelo. Es ms eciente el seleccionar solamente los campos que necesitas usar. dates(campo, tipo, orden) Retorna un QuerySet especial que evala a una lista de objetos datetime.datetime que representan todas las fechas disponibles de un cierto tipo en el contenido de la QuerySet. El argumento campo debe ser el nombre de un DateField o de un DateTimeField de tu modelo. El argumento tipo debe ser ya sea year, month o day. Cada objeto datetime.datetime en la lista de resultados es truncado de acuerdo al tipo provisto: ear" retorna una lista de todos los valores de aos distintos entre s para el campo.
2
"month" retorna una lista de todos los valores de aos/mes distintos entre s para el campo. "day" retorna una lista de todos los valores de aos/mes/da distintos entre s para el campo. orden, cuyo valor por omisin es ASC, debe ser ASC o DESC. El mismo especica cmo ordenar los resultados. Aqu tenemos algunos ejemplos:
>>> Entry.objects.dates( pub_date , year ) [datetime.datetime(2005, 1, 1)] >>> Entry.objects.dates( pub_date , month ) [datetime.datetime(2005, 2, 1), datetime.datetime(2005, 3, 1)] >>> Entry.objects.dates( pub_date , day )
310
[datetime.datetime(2005, 2, 20), datetime.datetime(2005, 3, 20)] >>> Entry.objects.dates( pub_date , day , order= DESC ) [datetime.datetime(2005, 3, 20), datetime.datetime(2005, 2, 20)] >>> Entry.objects.filter(headline__contains= Lennon ).dates( pub_date , day ) [datetime.datetime(2005, 3, 20)]
select_related() Retorna un QuerySet que seguir automticamente relaciones de clave fornea, seleccionando esos datos adicionales de objetos relacionados cuando ejecuta su consulta. Esto contribuye a la mejora de rendimiento que resulta en consultas (aveces mucho) ms grandes pero signican que el uso posterior de relaciones de clave fornea no requerirn consultas a la base de datos. Los siguientes ejemplos ilustran la diferencia select_related(). Esta es una bsqueda normal:
# Hits the database. >>> e = Entry.objects.get(id=5) # Hits the database again to get the related Blog object. >>> b = e.blog
entre
bsquedas
normales
bsquedas
select_related() sigue claves forneas tan lejos como le sea posible. Si tienes los siguientes modelos:
class City(models.Model): # ... class Person(models.Model): # ... hometown = models.ForeignKey(City) class Book(models.Model): # ... author = models.ForeignKey(Person)
entonces una llamada a Book.objects.select_related().get(id=4) colocar en el cache la Person relacionada y la City relacionada:
>>> b = Book.objects.select_related().get(id=4) >>> p = b.author # Doesnt hit the database. >>> c = p.hometown # Doesnt hit the database. >>> b = Book.objects.get(id=4) # No select_related() in this example. >>> p = b.author # Hits the database. >>> c = p.hometown # Hits the database.
Notar que select_related no sigue claves forneas que tienen null=True. Usualmente, el usar select_related() puede mejorar muchsimo el desempeo porque tu aplicacin puede puede entonces evitar muchas llamadas a la base de datos. Sin embargo, en siuaciones con conjuntos de relaciones profundamente anidadas, select_related() puede en algunos casos 26.5. Filtrando objetos 311
terminar siguiendo demasiadas relaciones y puede generar consultas tan grandes que terminan siendo lentas. extra() A veces, el lenguaje de consulta de Django no puede expresar facilmente clusulas WHERE complejas. Para estos casos extremos, Django provee un modicador de QuerySet llamado extra() una forma de inyectar clusulas especicas dentro del SQL generado por un QuerySet. Por denicin, estas consultas especiales pueden no ser portables entre los distintos motores de bases de datos (debido a que ests escribiendo cdigo SQL explcito) y violan el principio DRY, as que deberas evitarlas de ser posible. Se puede especicar uno o ms de params, select, where, o tables. Ninguno de los argumentos es obligatorio, pero deberas indicar al menos uno. El argumento select permite indicar campos adicionales en una clusula de SELECT. Debe contener un diccionario que mapee nombres de atributo a clusulas SQL que se utilizarn para calcular el atributo en cuestin:
>>> Entry.objects.extra(select={ is_recent : " pub_date > 2006-01-01 " })
Como resultado, cada objeto Entry tendr en este caso un atributo adicional, is_recent, un booleano que representar si el atributo pub_date del entry es mayor que el 1 de Enero de 2006. El siguiente ejemplo es ms avanzado; realiza una subconsulta para darle a cada objeto Blog resultante un atributo entry_count, un entero que indica la cantidad de objetos Entry asociados al blog:
>>> subq = SELECT COUNT(*) FROM blog_entry WHERE blog_entry.blog_id = blog_blog.id >>> Blog.objects.extra(select={ entry_count : subq})
(En este caso en particular, estamos aprovechando el hecho de que la consulta ya contiene la tabla blog_blog en su clusula FROM.) Tambin es posible denir clusulas WHERE explcitas quizs para realizar joins implcitos usando el argumento where. Se puede agregar tablas manualmente a la clusula FROM del SQL usando el argumento tables. Tanto where como tables reciben una lista de cadenas. Todos los argumentos de where son unidos con AND a cualquier otro criterio de bsqueda:
>>> Entry.objects.extra(where=[ id IN (3, 4, 5, 20) ])
Los parmetros select y where antes descriptos pueden utilizar los comodines normales para bases de datos en Python: %s para indicar parmetros que deberan ser escapados automticamente por el motor de la base de datos. El argumento params es una lista de los parmetros que sern utilizados para realizar la sustitucin:
>>> Entry.objects.extra(where=[ headline= %s ], params=[ Lennon ])
Siempre se debe utilizar params en vez de utilizar valores directamente en select o where ya que params asegura que los valores sern escapados correctamente de acuerdo con tu motor de base de datos particular. Este es un ejemplo de lo que est incorrecto:
Entry.objects.extra(where=[ " headline= %s " % name])
312
La excepcin DoesNotExist hereda de django.core.exceptions.ObjectDoesNotExist, as que puedes protegerte de mltiples excepciones DoesNotExist:
>>> from django.core.exceptions import ObjectDoesNotExist >>> try: ... e = Entry.objects.get(id=3) ... b = Blog.objects.get(id=1) ... except ObjectDoesNotExist: ... print " Either the entry or blog doesn t exist. "
create(**kwargs) Este mtodo sirve para crear un objeto y guardarlo en un mismo paso. Te permite abreviar dos pasos comunes:
>>> p = Person(first_name= " Bruce " , last_name= " Springsteen " ) >>> p.save()
get_or_create(**kwargs) Este mtodo sirve para buscar un objeto y crearlo si no existe. Devuelve una tupla (object, created), donde object es el objecto encontrado o creado, y created es un booleano que indica si el objeto fue creado. Est pensado como un atajo para el caso de uso tpico y es ms que nada til para scripts de importacin de datos, por ejemplo:
try: obj = Person.objects.get(first_name= John , last_name= Lennon ) except Person.DoesNotExist: obj = Person(first_name= John , last_name= Lennon , birthday=date(1940, 10, 9)) obj.save()
Este patrn se vuelve inmanejable a medida que aumenta el nmero de campos en el modelo. El ejemplo anterior puede ser escrito usando get_or_create as:
313
Cualquier argumento que se le pase a get_or_create() excepto el argumento opcional defaults ser utilizado en una llamada a get(). Si se encuentra un objecto, get_or_create devolver una tupla con ese objeto y False. Si no se encuentra un objeto, get_or_create() instanciar y guardar un objeto nuevo, devolviendo una tupla con el nuevo objeto y True. El nuevo objeto ser creado de acuerdo con el siguiente algoritmo:
defaults = kwargs.pop( defaults , {}) params = dict([(k, v) for k, v in kwargs.items() if __ not in k]) params.update(defaults) obj = self.model(**params) obj.save()
Esto es, se comienza con los argumentos que no sean defaults y que no contengan doble guin bajo (lo cual indicara una bsqueda no exacta). Luego se le agrega el contenido de defaults, sobreescribiendo cualquier valor que ya estuviera asignado, y se usa el resultado como claves para el constructor del modelo. Si el modelo tiene un campo llamado defaults y es necesario usarlo para una bsqueda exacta en get_or_create(), simplemente hay que utilizar defaults__exact as:
Foo.objects.get_or_create( defaults__exact = bar , defaults={ defaults : baz } )
Nota: Como ya se mencion, get_or_create es utilizado ms que nada en scripts que necesiten procesar datos y crear nuevos campos si los que existen no estn disponibles. Si necesitas utilizar get_or_create() en una vista, por favor asegurate de utilizarlo solo en pedidos POST salvo que tengas una buena razn para no hacerlo. Los pedidos GET no deberan afectar los datos de ninguna manera; se debe utilizar POST en cualquier pedido a una pgina que pueda tener como efecto secundario una modicacin a tus datos.
count() Devuelve un entero representando el nmero de objetos en la base de datos que coincidan con el QuerySet. count() nunca levanta excepciones. He aqu un ejemplo:
# Returns the total number of entries in the database. >>> Entry.objects.count() 4 # Returns the number of entries whose headline contains Lennon >>> Entry.objects.filter(headline__contains=Lennon).count() 1
count() en el fondo realiza un SELECT COUNT(*), as que deberas siempre utilizar count() en vez de cargar todos los registros en objetos Python y luego invocar len() sobre el resultado. Dependiendo de la base de datos que ests utilizando (e.g., PostgreSQL o MySQL), count() podra devolver un entero largo en vez de un entero normal de Python. Esto es una caracterstica particular de la implementacin subyacente que no debera ser ningn problema en la vida real.
314
in_bulk(id_list) Este mtodo toma una lista de claves primarias y devuelve un diccionario que mapea cada clave primaria en una instancia con el ID dado, por ejemplo:
>>> {1: >>> {1: >>> {} Blog.objects.in_bulk([1]) Beatles Blog} Blog.objects.in_bulk([1, 2]) Beatles Blog, 2: Cheddar Talk} Blog.objects.in_bulk([])
Si no se encuentra un objeto en la base para un ID en particular, este id no aparecer en el diccionario resultante. Si le pasas una lista vaca a in_bulk(), obtendrs un diccionario vaco. latest(eld_name=None) Devuelve el ltimo objeto de la tabla, ordenados por fecha, utilizando el campo que se provea en el argumento field_name como fecha. Este ejemplo devuelve el Entry ms reciente en la tabla, de acuerdo con el campo pub_date:
>>> Entry.objects.latest( pub_date )
Si el Meta de tu modelo especica get_latest_by, se puede omitir el argumento field_name. Django utilizar el campo indicado en get_latest_by por defecto. Al igual que get(), latest() levanta DoesNotExist si no existe un objeto con los parmetros provistos.
Si se suministra un argumento de palabra clave invlido, la funcin levantar una excepcin de TypeError. A continuacin se listan los tipos de bsqueda que existen.
26.6.1 exact
Realiza una bsqueda por coincidencias exactas:
>>> Entry.objects.get(headline__exact= " Man bites dog " )
Esto busca objetos que tengan en el campo headline la frase exacta Man bites dog. Si no se suministra un tipo de bsqueda O sea, si tu argumento de palabra clave no contiene un doble guin bajo el tipo de bsqueda se asume como exact. 26.6. Patrones de bsqueda 315
Esto es por conveniencia, dado que las bsquedas con tipo de bsqueda exact son las ms frecuentes.
26.6.2 iexact
Realiza una bsqueda por coincidencias exactas sin distinguir maysculas de minsculas:
>>> Blog.objects.get(name__iexact= beatles blog )
Traer objetos con nombre Beatles Blog, beatles blog, BeAtLes BLoG, etctera.
26.6.3 contains
Realiza una bsqueda de subcadenas, distinguiendo maysculas y minsculas:
Entry.objects.get(headline__contains= Lennon )
Esto coincidir con el titular Today Lennon honored pero no con today lennon honored. SQLite no admite sentencias LIKE distinguiendo maysculas y minsculas; cuando se utiliza SQLite, contains se comporta como icontains. Escapado de porciento y guin bajo en sentencias LIKE Los patrones de bsqueda que resulten en sentencias SQL LIKE (iexact, contains, icontains, startswith, istartswith, endswith, y iendswith) escaparn automticamente los dos caracteres especiales utilizados en sentencias LIKE el porciento y el guin bajo. (En una sentencia LIKE, el smbolo de porciento indica una secuencia de caracteres cualesquiera, y el guin bajo indica un solo caracter cualquiera). Esto signica que las cosas deberan funcionar de manera intuitiva, por que la abstraccin funciona bien. Por ejemplo, para obtener todos los Entries que contengan un smbolo de porciento, simplemente hace falta utilizar el smbolo de porcentaje como cualquier otro caracter:
Entry.objects.filter(headline__contains= % )
Django se hace cargo del escapado. El SQL resultante ser algo similar a esto:
SELECT ... WHERE headline LIKE %\ % %;
Lo mismo vale para el guin bajo. Tanto el smbolo de porcentaje como el guin bajo se deberan manejar de manera transparente.
26.6.4 icontains
Realiza una bsqueda de subcadenas, sin distinguir maysculas y minsculas:
>>> Entry.objects.get(headline__icontains= Lennon )
316
Estas consultas devuelven cualquier objeto con un ID mayor a 4, un ID menor a 15, y un ID mayor o igual a 1, respectivamente. Por lo general estos operadores se utilizarn con campos numricos. Se debe tener cuidado con los campos de caracteres, ya que el orden no siempre es el que uno se esperara (i.e., la cadena 4 resulta ser mayor que la cadena 10).
26.6.6 in
Aplica un ltro para encontrar valores en una lista dada:
Entry.objects.filter(id__in=[1, 3, 4])
26.6.7 startswith
Busca coincidencias de prejos distinguiendo maysculas y minsculas:
>>> Entry.objects.filter(headline__startswith= Will )
Esto encontrar los titulares Will he run? y Willbur named judge, pero no Who is Will? o will found in crypt.
26.6.8 istartswith
Realiza una bsqueda por prejos, sin distinguir maysculas y minsculas:
>>> Entry.objects.filter(headline__istartswith= will )
Esto devolver los titulares Will he run?, Willbur named judge, y will found in crypt, pero no Who is Will?
26.6.10 range
Realiza una bsqueda por rango:
317
>>> start_date = datetime.date(2005, 1, 1) >>> end_date = datetime.date(2005, 3, 31) >>> Entry.objects.filter(pub_date__range=(start_date, end_date))
Se puede utilizar range en cualquier lugar donde podras utilizar BETWEEN en SQL para fechas, nmeros, e incluso cadenas de caracteres.
26.6.12 isnull
Toma valores True o False, que correspondern a consultas SQL de IS NULLy IS NOT NULL, respectivamente:
>>> Entry.objects.filter(pub_date__isnull=True)
__isnull=True vs. __exact=None Hay una diferencia importante entre __isnull=True y __exact=None. __exact=None siempre devolver como resultado un conjunto vaco, ya que SQL requiere que ningn valor sea igual a NULL. __isnull determina si el campo actualmente contiene un valor NULL sin realizar la comparacin.
26.6.13 search
Un booleano que realiza bsquedas full-text, que aprovecha el indexado full-text. Esto es como contains pero signicativamente ms rpido debido al indexado full-text. Ntese que este tipo de bsqueda slo est disponible en MySQL y requiere de manipulacin directa de la base de datos para agregar el ndice full-text.
318
El uso de pk no se limita a bsquedas __exact cualquier patrn de bsqueda puede ser combinado con pk para realizar una bsqueda sobre la clave primaria de un modelo:
# Buscar entradas en blogs con id 1, 4, o 7 >>> Blog.objects.filter(pk__in=[1,4,7]) # Buscar entradas en blogs con id > 14 >>> Blog.objects.filter(pk__gt=14)
Las bsquedas pk tambin funcionan con joins. Por ejemplo, estas tres sentencias son equivalentes:
>>> Entry.objects.filter(blog__id__exact=3) # Forma explcita >>> Entry.objects.filter(blog__id=3) # __exact implcito >>> Entry.objects.filter(blog__pk=3) # __pk implica __id__exact
Los objetos Q pueden ser combinados utilizando los operadores & y |. Cuando se utiliza un operador sobre dos objetos, se obtiene un nuevo objeto Q. Por ejemplo, un OR de dos consultas question__startswith sera:
Q(question__startswith= Who ) | Q(question__startswith= What )
Puede componer sentencias de complejidad arbitraria combinando objetos Q con los operadores & y |. Tambin se pueden utilizar parntesis para agrupar. Cualquier funcin de bsqueda que tome argumentos de palabra clave (e.g., filter(), exclude(), get()) puede recibir tambin uno o ms objetos Q como argumento posicional (no nombrado). Si se proveen multiples objetos Q como argumentos a una funcin de bsqueda, los argumentos sern unidos con AND, por ejemplo:
Poll.objects.get( Q(question__startswith= Who ), Q(pub_date=date(2005, 5, 2)) | Q(pub_date=date(2005, 5, 6)) )
Las funciones de bsqueda pueden adems mezclar el uso de objetos Q y de argumentos de palabra clave. Todos los argumentos provistos a una funcin de bsqueda (sean argumentos de palabra clave u objetos Q) son unidos con AND. Sin embargo, si se provee un objeto Q debe preceder la denicin de todos los argumentos de palabra clave. Por ejemplo, lo siguiente:
319
Este camino puede ser tan largo como quieras. Tambin Funciona en la otra direccin. Para referirse a una relacin inversa, simplemente hay que utilizar el nombre en minsculas del modelo. Este ejemplo busca todos los objetos Blog que tengan al menos un Entry cuyo headline contenga Lennon:
>>> Blog.objects.filter(entry__headline__contains= Lennon )
320
Se puede acceder y asignar el valor de la clave fornea va el atributo. Como sera de esperar, los cambios a la clave fornea no se guardan en el modelo hasta que invoques el mtodo save(), por ejemplo:
e = Entry.objects.get(id=2) e.blog = some_blog e.save()
Si un campo ForeignKey tiene la opcin null=True seteada (i.e. permite valores NULL), se le puede asignar None:
e = Entry.objects.get(id=2) e.blog = None e.save() # "UPDATE blog_entry SET blog_id = NULL ...;"
El acceso a relaciones uno-a-muchos se almacena la primera vez que se accede al objeto relacionado. Cualquier acceso subsiguiente a la clave fornea del mismo objeto son cacheadas, por ejemplo:
e = Entry.objects.get(id=2) print e.blog # Busca el Blog asociado en la base de datos. print e.blog # No va a la base de datos; usa la versin cacheada.
Notar que el mtodo de QuerySet select_related() busca inmediatamente todos los objetos de relaciones uno-a-muchos de la instancia:
e = Entry.objects.select_related().get(id=2) print e.blog # No va a la base de datos; usa la versin cacheada. print e.blog # No va a la base de datos; usa la versin cacheada.
select_related() est documentada en la seccin Mtodos de consulta que retornan nuevos QuerySets.
Se puede cambiar el nombre del atributo FOO_set indicando el parmetro related_name en la denicin del ForeignKey(). Por ejemplo, si el modelo Entry fuera cambiado por blog = ForeignKey(Blog, related_name=entries), el ejemplo anterior pasara a ser as:
b = Blog.objects.get(id=1) b.entries.all() # Encontrar todos los objetos Entry relacionados a b. # b.entries es un Manager que devuelve QuerySets. b.entries.filter(headline__contains= Lennon ) b.entries.count()
No se puede acceder al Manager de ForeignKey inverso desde la clase misma; debe ser accedido desde una instancia: 26.8. Objetos Relacionados 321
Adems de los metodos de QuerySet denidos en la seccin Recuperando Objetos, el Manager de ForeignKey tiene los siguientes mtodos adicionales: add(obj1, obj2, ...): Agrega los objetos del modelo indicado al conjunto de objetos relacionados, por ejemplo:
b = Blog.objects.get(id=1) e = Entry.objects.get(id=234) b.entry_set.add(e) # Associates Entry e with Blog b.
create(**kwargs): Crea un nuevo objeto, lo guarda, y lo deja en el conjunto de objetos relacionados. Devuelve el objeto recin creado:
b = Blog.objects.get(id=1) e = b.entry_set.create(headline= Hello , body_text= Hi , pub_date=datetime.date(2005, # No hace falta llamar a e.save() ac -- ya ha sido guardado
b = Blog.objects.get(id=1) e = Entry(blog=b, headline=Hello, body_text=Hi, pub_date=datetime.date(2005, 1, 1) e.save() Notar que no es necesario especificar el argumento de palabra clave correspondiente al modelo que define la relacin. En el ejemplo anterior, no le pasamos el parmetro blog a create(). Django deduce que el campo blog del nuevo Entry debera ser b.
remove(obj1, obj2, ...): Quita los objetos indicados del conjunto de objetos relacionados:
b = Blog.objects.get(id=1) e = Entry.objects.get(id=234) b.entry_set.remove(e) # Desasociar al Entry e del Blog b. Para evitar inconsistencias en la base de datos, este mtodo slo existe para objetos ForeignKey donde null=True. Si el campo relacionado no puede pasar ser None (NULL), entonces un objeto no puede ser quitado de una relacin sin ser agregado a otra. En el ejemplo anterior, el quitar a e de b.entry_set() es equivalente a hacer e.blog = None, y dado que la definicin del campo ForeignKey blog (en el modelo Entry) no indica null=True, esto es una accin invlida.
Para asignar todos los miembros de un conjunto relacionado en un solo paso, simplemente se le asigna al conjunto un objeto iterable, por ejemplo:
b = Blog.objects.get(id=1) b.entry_set = [e1, e2]
Si el mtodo clear() est denido, todos los objetos pre-existentes sern quitados del entry_set antes de que todos los objetos en el iterable (en este caso, la lista) sean agregados al conjunto. Si el
322
mtodo clear() no est disponible, todos los objetos del iterable son agregados al conjunto sin quitar antes los objetos pre-existentes. Todas las operaciones inversas denidas en esta seccin tienen efectos inmediatos en la base de datos. Toda creacin, borradura y agregado son inmediata y automticamente grabados en la base de datos.
Al igual que los campos ForeignKey, los ManyToManyField pueden indicar un related_name. En el ejemplo anterior, si el campo ManyToManyField en el modelo Entry indicara related_name=entries, cualquier instancia de Author tendra un atributo entries en vez de entry_set. Cmo son posibles las relaciones inversas? El mapeador objeto-relacional requiere que denas relaciones en ambos extremos. Los desarrolladores Django creen que esto es una violacin del principio DRY (Dont Repeat Yourself), as que Django slo te exige que denas la relacin en uno de los extremos. Pero cmo es esto posible, 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 INSTALLED_APPS. La primera vez que se carga cualquier modelo, Django itera sobre todos los modelos en INSTALLED_APPS y crea las relaciones inversas en memoria como sea necesario. Esencialmente, una de las funciones de INSTALLES_APPS es indicarle a Django el dominio completo de modelos que se utiliza.
323
Tambin se puede borrar objetos en grupo. Todo objeto QuerySet tiene un mtodo delete() que borra todos los miembros de ese QuerySet. Por ejemplo, esto borra todos los objetos Entry que tengan un ao de pub_date igual a 2005:
Entry.objects.filter(pub_date__year=2005).delete()
Cuando Django borra un objeto, emula el comportamiento de la restriccin de SQL ON DELETE CASCADE en otras palabras, todos los objetos que tengan una clave fornea que apunte al objeto que est siendo borrado sern borrados tambin, por ejemplo:
b = Blog.objects.get(pk=1) # Esto borra el Blog y todos sus objetos Entry. b.delete()
Notar que delete() es el nico mtodo de QuerySet que no est expuesto en el Manager mismo. Esto es un mecanismo de seguridad para evitar que accidentalmente solicites Entry.objects.delete() y borres todos los Entry. Si realmente quieres borrar todos los objetos, hay que pedirlo explcitamente al conjunto completo de objetos:
Entry.objects.all().delete()
26.10.1 get_FOO_display()
Por cada campo que indica la opcin choices, el objeto tendr un mtodo get_FOO_display(), donde FOO es el nombre del campo. Este mtodo devuelve el valor humanamente legible del campo. Por ejemplo, en el siguiente modelo:
GENDER_CHOICES = ( ( M , Male ), ( F , Female ), ) class Person(models.Model): name = models.CharField(max_length=20) gender = models.CharField(max_length=1, choices=GENDER_CHOICES)
324
26.10.3 get_FOO_lename()
Todo campo FileField le dar al objeto un mtodo get_FOO_filename(), donde FOO es el nombre del campo. Esto devuelve el nombre de archivo completo en el sistema de archivos, de acuerdo con la variable MEDIA_ROOT. Notar que el campo ImageField es tcnicamente una subclase de FileField, as que todo modelo que tenga un campo ImageField obtendr tambin este mtodo.
26.10.4 get_FOO_url()
Por todo campo FileField el objeto tendr un mtodo get_FOO_url(), donde FOO es el nombre del campo. Este mtodo devuelve la URL al archivo, de acuerdo con tu variable MEDIA_URL. Si esta variable est vaca, el mtodo devolver una cadena vaca.
26.10.5 get_FOO_size()
Por cada campo FileField el objeto tendr un mtodo get_FOO_size(), donde FOO es el nombre del campo. Este mtodo devuelve el tamao del archivo, en bytes. (La implementacin de este mtodo utiliza os.path.getsize.)
325
lizados par simplicar el proceso de escribir vistas. Estas funciones se pueden hallar en el mdulo django.shortcuts.
26.11.1 get_object_or_404()
Un modismo frecuente es llamar a get() y levantar un Http404 si el objeto no existe. Este modismo es capturado en la funcin get_object_or_404(). Esta funcion toma un modelo Django como su primer argumento, y una cantidad arbitraria de argumentos de palabra clave, que le pasa al mtodo get() del Manager por defecto del modelo. Luego levanta un Http404 si el objeto no existe, por ejemplo:
# Get the Entry with a primary key of 3 e = get_object_or_404(Entry, pk=3)
Cuando se le pasa un modelo a esta funcin, se utiliza el Manager por defecto para ejecutar la consulta get() subyacente. Si no quieres que se utilice el manager por defecto, o si quiere buscar en una lista de objetos relacionados, se le puede pasar a get_object_or_404() un objeto Manager en vez:
# Get the author of blog instance e with a name of Fred a = get_object_or_404(e.authors, 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.recent_entries, pk=3)
26.11.2 get_list_or_404()
get_list_or_404() se comporta igual que get_object_or_404(), salvo porque llama a filter() en vez de a get(). Levanta un Http404 si la lista resulta vaca.
326
CAPTULO 27
El Captulo 9 es una introduccin a las vistas genricas, pero pasa por alto algunos detalles . Este apndice describe todas las vistas genricas, junto con las opciones que cada una de ellas puede aceptar. Antes de intentar entender este material de referencia es muy conveniente leer el Captulo 9 . Tampoco viene mal un repaso a los modelos Book, Publisher y Author denidos en dicho captulo, ya que sern usados en los ejemplo incluidos en esta apndice.
327
Argumento allow_empty
Descripcin Un valor booleano que indica como debe comportarse la vista si no hay objetos disponibles. Si vale False y no hay objetos, la vista elevar un error 404 en vez de mostrar una pgina vaca. Su valor por defecto es Falsa. context_processors lista de procesadores de contexto adicionales (adems de los incluidos por Es una el sistema), que se aplican a la plantilla de la vista. En el Captulo 10 se explica con detalle la lista de procesadores de contexto adicionales. extra_contextUn diccionario cuyos valores se aaden al contexto de la plantilla. Si se almacena un objeto que sea invocable, la vista genrica lo ejecutar justo antes de representar la plantilla mimetype El tipo MIME a usar para el documento resultante. Por defecto utiliza el tipo denido en la variable de conguracin DEFAULT_MIME_TYPE, cuyo valor inicial es text/html. queryset Un objeto de tipo QuerySet (por ejemplo, Author.objects.all()) del cual se leern los objetos a utilizar por la vista. En el apndice C hay ms informacin acerca de los objetos QuerySet. La mayora de las vistas genricas necesitan este argumento. template_loadercargador de plantillas a utilizar. Por defecto es django.template.loader. El Vase el Captulo 10_ donde se da ms informacin acerca de los cargadores de plantillas. template_nameEl nombre completo de la plantilla a usar para representar la pgina. Este argumento se puede usar si queremos modicar el nombre que se genera automticamente a partir del QuerySet. template_object_name de la variable principal en el contexto de la plantilla. Por defecto, es El nombre object. Para las listas que utilizan ms de objeto (por ejemplo, las vistas de listados o de archivos por fechas), se aade el sujo _list al valor de este parmetro, as que si no se indica nada y la vista utiliza varios objetos, estos estarn accesibles mediante una variable llamada object_list.
Una peticin a /foo/ mostrara la plantilla foo_index.html, y una solicitud a /foo/15/ mostrara foo_detail.html con una variable de contexto {{ params.id }} cuyo valor sera 15. 328 Captulo 27. Apndice D: Referencia de las vistas genricas
Este ejemplo devuelve una respuesta Gone para cualquier peticin a /bar/:
from django.views.generic.simple import redirect_to urlpatterns = patterns( django.views.generic.simple , ( ^bar/$ , redirect_to, { url : None}), )
Argumentos obligatorios url: La URL a la que redirigir, en forma de cadena de texto, o None si quereremos devolver una respuesta 410 (Gone segn el estndar HTTP).
329
from mysite.books.models import Author from django.conf.urls.defaults import * from django.views.generic import list_detail author_list_info = { queryset : } urlpatterns = patterns( , ( r authors/$ , list_detail.object_list, author_list_info) )
Author.objects.all(),
allow_empty : True,
Argumentos obligatorios queryset: Un QuerySet de los objetos a listar (Vase la table D-1). Argumentos opcionales paginate_by: es un nmero entero que especica cuantos objetos se deben mostrar en cada pgina. Segn se especique en este parmetro, los resultados sern paginados, de forma que se distribuirn por varias pginas de resultado. La vista determinar que pgina de resultados debe mostrar o bien desde un parmetro page incluido en la URL (va Get) o mediante una variable page especicada en el URLconf. En cualquiera de los dos casos, el ndice comienza en cero. En la siguiente seccin hay una nota sobre paginacin donde se explica con un poco ms de detalle este sistema. Adems, esta vidta acepta cualquiera de los siguientes argumentos opcionales descritos en 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 el parmetro opcional template_name, la vista usar una plantilla llamada <app_label>/<model_name>_list.html. Tanto la etiqueta de la aplicacin como la etiqueta del modelo se obtienen del parmetro queryset. La etiqueta de aplicacin es el nombre de la aplicacin en que se ha denido el modelo, y la etiqueta de modelo es el nombre, en minsculas, de la clase del modelo. En el ejemplo anterior, tendriamos que el queryset sera Author.objects.all(), por lo que la etiqueta de la aplicacin ser books y el nombre del modelo es author. Con esos datos, el nombre de la plantilla a utilizar por defecto ser books/author_list.html. Contexto de plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores:
330
object_list: La lista de los objetos. El nombre de la variable viene determinado por el parmetro template_object_name, y vale object por defecto. Si se deniera template_object_name como foo, el nombre de esta variable sera foo_list. is_paginated: Un valor booleano que indicar si los resultados sern paginados o no. Concretamente, valdr False si el nmero de objetos disponibles es inferior o igual a paginate_by. Si los resultados estn paginados, el contexto dispondr tambin de estas variables: results_per_page: El nmero de objetos por pgina. (Su valor es el mismo que el del parmetro paginate_by). has_next: Un valor booleano indicando si hay una siguiente pgina. has_previous: Un valor booleano indicando si hay una pgina previa. page: El nmero de la pgina actual, siendo 1 la primera pgina. next: El nmero de la siguiente pgina. Incluso si no hubiera siguiente pgina, este valor seguir siendo un numero entero que apuntara a una hipottica siguiente pgina. Tambin utiliza un ndice basado en 1, no en cero. previous: El nmero de la anterior pgina, usando un ndice basado en 1, no en cero. pages: El nmero total de pginas. hits: El nmero total de objetos en todas las pginas, no slo en la actual. Si se utiliza el parmetro paginate_by, Django paginar los resultados. Puedes indicar qu pagina visualizar usando dos mtodos diferentes: Usar un parmetro page en el URLconf. Por ejemplo, tu URLconf podra parecerse a este:
( r ^objects/page(?P<page>[0-9]+)/$ , object_list , dict(info_dict))
Pasar el nmero de la pgina mediante un parmetro page en la URL: Por ejemplo, tus URL se podran parecer a esto:
/objects/?page=3
En ambos casos, page es un ndice basado en 1, lo que signica que la primera pgina siempre ser la nmero 1, no la nmero 0.
331
Argumentos obligatorios queryset: Un QuerySet que ser usado para localizar el objeto a mostrar (vase la Tabla D-1). y luego hace falta, o un: object_id: El valor de la clave primaria del objeto a mostrar. o bien: slug: La etiqueta o slug del objeto en cuestin. Si se usa este sistema de identicacin, hay que emplear obligatoriamente el argumento slug_field (que se explica en la siguiente seccin). Argumentos opcionales slug_field: El nombre del atributo del objeto que contiene el slug. Es obligatorio si ests usando el argumento slug, y no se debe usar si ests usando el argumento object_id. template_name_field: El nombre de un atributo del objeto cuyo valor se usar como el nombre de la plantilla a utilizar. De esta forma, puedes almacenar en tu objeto la plantilla a usar. En otras palabras, si tu objeto tiene un atributo the_template que contiene la cadena de texto foo.html, y denes template_name_field para que valga the_template, entonces la vista genrica de este objeto usar como plantilla foo.html. Si el atributo indicado por template_name_field no existe, se usara el indicado por el argumento template_name. Es un mecanismo un poco enmaraado, pero puede ser de mucha ayuda en algunos casos. Esta vista tambin acepta estos argumentos comunes (Vase 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, se usar la plantilla <app_label>/<model_name>_detail.html. Contexto de plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: object: El objeto. El nombre de esta variable puede ser distinto si se ha especicado el argumento template_object_name, cuyo valor es object por defecto. Si denimos template_object_name como foo, el nombre de la variable ser foo. 332 Captulo 27. Apndice D: Referencia de las vistas genricas
: Book.objects.all(),
Argumentos obligatorios date_field: El nombre de un campo DateField o DateTimeField de los objetos que componen el QuerySet. La vista usar los valores de ese campo como referencia para obtener los ltimos objetos. queryset: El QuerySet de objetos que forman el archivo.
333
Argumentos opcionales allow_future: Un valor booleano que indica si los objetos futuros (es decir, con fecha de referencia en el futuro) deben aparecer o no. num_latest: El nmero de objetos que se deben enviar a la plantilla. Su valor por defecto es 15. Esta vista tambin acepta estos argumentos comunes (Vase 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, <app_label>/<model_name>_archive.html. Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: date_list: Una lista de objetos de tipo datetime.date que representaran todos los aos en los que hay objetos, de acuerdo al queryset. Vienen ordenados de forma descendente, los aos mas recientes primero. Por ejemplo, para un blog que tuviera entradas desde el ao 2003 hasta el 2006, la lista contendr cuatro objetos de tipo datetime.date, uno para cada uno se esos aos. latest: Los ltimos num_latest objetos en el sistema, considerndolos ordenados de forma descendiente por el campo date_field de referencia. Por ejemplo, si num_latest vale 10, entonces latest ser una lista de los ltimos 10 objetos contenidos en el queryset. se usar la plantilla
334
Argumentos obligatorios date_field: Igual que en archive_index (Vase la seccin previa). queryset: El QuerySet de objetos archivados. year: El ao, con cuatro dgitos, que la vista usar para mostrar el archivo (Como se ve en el ejemplo, normalmente se obtiene de un parmetro en la URL). Argumentos opcionales make_object_list: Un valor booleano que indica si se debe obtener la lista completa de objetos para este ao y pasrsela a la plantilla. Si es True, la lista de objetos estar disponible para la plantilla con el nombre de object_list (Aunque este nombre podra ser diferente; vase la informacin sobre object_list en la siguiente explicacin sobre Contexto de plantilla). Su valor por defecto es False. allow_future: Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el futuro. Esta vista tambin acepta los siguientes argumentos comunes (Vase 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 ningn valor en template_name, <app_label>/<model_name>_archive_year.html. Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: date_list: Una lista de objetos de tipo datetime.date, que representan todos los meses en los que hay disponibles objetos en un ao determinado, de acuerdo al contenido del queryset, en orden ascendente. year: El ao a mostrar, en forma de cadena de texto con cuatro dgitos. object_list: Si el parmetro make_object_list es True, esta variable ser una lista de objetos cuya fecha de referencia cae en en ao a mostrar, ordenados por fecha. El nombre de la variable depende del parmetro template_object_name, que es object por defecto. Si template_object_name fuera foo, el nombre de esta variable sera foo_list. la vista usar la plantilla
335
Argumentos obligatorios year: El ao a mostrar, en forma de cadena de texto con cuatro dgitos. month: El mes a mostrar, formateado de acuerdo con el argumento month_format. queryset: El QuerySet de objetos archivados. date_field: El nombre del campo de tipo DateField o DateTimeField en el modelo usado para el QuerySet que se usar como fecha de referencia. Argumentos opcionales month_format: Una cadena de texto que determina el formato que debe usar el parmetro month. La sintaxis a usar debe coincidir con la de la funcin time.strftime (La documentacin de esta funcin se puede consultar en http://www.djangoproject.com/r/python/strftime/). Su valor por defecto es " %b", que signica el nombre del mes, en ingls, y abreviado a tres letras (Es decir, jan, feb, etc.). Para cambiarlo de forma que se usen nmeros, hay que utilizar como cadena de formato " %m". allow_future: Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el futuro, igual al que hemos visto en otras vistas anteriores. Esta vista tambin acepta los siguientes argumentos comunes (Vase la Tabla D-1): allow_empty context_processors extra_context mimetype template_loader template_name template_object_name
336
Nombre de la plantilla Si no se especica ningn valor en template_name, <app_label>/<model_name>_archive_month.html. Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: month: Un objeto de tipo datetime.date que representa el mes y ao de referencia. next_month: Un objeto de tipo datetime.date que representa el primer da del siguiente mes. Si el siguiente mes cae en el futuro, valdr None. previous_month: Un objeto de tipo datetime.date que representa el primer da del mes anterior. Al contrario que next_month, su valor nunca ser None. object_list: Una lista de objetos cuya fecha de referencia cae en en ao y mes a mostrar. El nombre de la variable depende del parmetro template_object_name, que es object por defecto. Si template_object_name fuera foo, el nombre de esta variable sera foo_list. la vista usar como plantilla
Ejemplo
urlpatterns = patterns( , # ... **(** ** r ^(?P<year> \ d{4})/(?P<week> \ d{2})/$ ,** **date_based.archive_week,** **book_info** **),** )
Argumentos obligatorios year: El ao, con cuatro dgitos (Una cadena de texto). week: La semana del ao (Una cadena de texto). queryset: El QuerySet de los objetos archivados. date_field: El nombre del campo de tipo DateField o DateTimeField en el modelo usado para el QuerySet que se usar como fecha de referencia.
337
Argumentos opcionales allow_future: Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el futuro. Esta vista tambin acepta los siguientes argumentos comunes (Vase 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 ningn valor en template_name la vista usar como plantilla <app_label>/<model_name>_archive_week.html. Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: week: Un objeto de tipo datetime.date, cuyo valor es el primer da de la semana considerada. object_list: Una lista de objetos disponibles para la semana en cuestin. El nombre de esta variable depende del parmetro template_object_name, que es object por defecto. Si template_object_name fuera foo, el nombre de esta variable sera foo_list.
Argumentos obligatorios year: El ao, con cuatro dgitos (Una cadena de texto). month: El mes, formateado de acuerdo a lo indicado por el argumento month_format day: El da, formateado de acuerdo al argumento day_format. 338 Captulo 27. Apndice D: Referencia de las vistas genricas
queryset: El QuerySet de los objetos archivados. date_field: El nombre del campo de tipo DateField o DateTimeField en el modelo usado para el QuerySet que se usar como fecha de referencia. Argumentos opcionales month_format: Una cadena de texto que determina el formato que debe usar el parmetro month. Hay una explicacin ms detallada en la seccion de Archivos mensuales, incluida anteriormente. day_format: Equivalente a month_format, pero para el da. Su valor por defecto es " %d" (que es el da del mes como nmero decimal y relleno con ceros de ser necesario; 01-31). allow_future: Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el futuro. Esta vista tambin acepta los siguientes argumentos comunes (Vase 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 ningn valor en template_name la vista usar como plantilla <app_label>/<model_name>_archive_day.html. Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: day: Un objeto de tipo datetime.date cuyo valor es el del da en cuestin. next_day: Un objeto de tipo datetime.date que representa el siguiente da. Si cae en el futuro, valdr None. previous_day: Un objeto de tipo datetime.date que representa el da anterior. Al contrario que next_day, su valor nunca ser None. object_list: Una lista de objetos disponibles para el da en cuestin. El nombre de esta variable depende del parmetro template_object_name, que es object por defecto. Si template_object_name fuera foo, el nombre de esta variable sera foo_list.
339
Ejemplo
urlpatterns = patterns( , # ... **( r ^books/today/$ , date_based.archive_today, book_info),** )
Ejemplo Esta vista tiene una (pequea) diferencia con las dems vistas basadas en fechas que hemos visto anteriormente, y es que necesita que le especiquemos de forma inequvoca el objeto en cuestin; esto lo podemos hacer con el identicador del objeto o con un campo de tipo slug. Como el objeto que estamos usando en el ejemplo no tiene ningn campo de tipo slug, usaremos el identicador para la URL. Normalmente se considera una buena prctica usar un campo slug, pero no lo haremos en aras de simplicar el ejemplo.
urlpatterns = patterns( , # ... **(** ** r ^(?P<year> \ d{4})/(?P<month>[a-z]{3})/(?P<day> \ d{2})/(?P<object_id>[ \ w-]+)/$ **date_based.object_detail,** **book_info** **),** )
Argumentos obligatorios year: El ao, con cuatro dgitos (Una cadena de texto). month: El mes, formateado de acuerdo a lo indicado por el argumento month_format day: El da, formateado de acuerdo al argumento day_format. queryset: El QuerySet que contiene el objeto. date_field: El nombre del campo de tipo DateField o DateTimeField en el modelo usado para el QuerySet que se usar como fecha de referencia. Y tambin habr que especicar, o bien un: object_id: El valor de la clave primaria del objeto.
340
o bien un: slug: El slug del objeto. Si se utiliza este argumento, es obligatorio especicar un valor para el argumento slug_field (que describiremos en la siguiente seccin). Argumentos opcionales allow_future: Un valor booleano que indica si deben incluirse o no en esta vista las fechas en el futuro. day_format: Equivalente a month_format, pero para el da. Su valor por defecto es " %d" (que es el da del mes como nmero decimal y relleno con ceros de ser necesario; 01-31). month_format: Una cadena de texto que determina el formato que debe usar el parmetro month. Hay una explicacin ms detallada en la seccion de Archivos mensuales, incluida anteriormente. slug_field: El nombre del atributo que almacena el valor del slug*. Es obligatorio incluirlo si se ha usado el argumento slug, y no debe aparecer si se ha especicado el argumento object_id. template_name_field: El nombre de un atributo del objeto cuyo valor se usar como el nombre de la plantilla a utilizar. De esta forma, puedes almacenar en tu objeto la plantilla a usar. En otras palabras, si tu objeto tiene un atributo the_template que contiene la cadena de texto foo.html, y denes template_name_field para que valga the_template, entonces la vista genrica de este objeto usar como plantilla foo.html. Esta vista tambin acepta los siguientes argumentos comunes (Vase la Tabla D-1): context_processors extra_context mimetype template_loader template_name template_object_name Nombre de la plantilla Si no se ha especicado ningn valor en template_name la vista usar como plantilla <app_label>/<model_name>_detail.html. Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: object: El object. El nombre de esta variable depende del parmetro template_object_name, que es object por defecto. Si template_object_name fuera foo, el nombre de esta variable sera foo.
341
Estas vistas pueden cambiar ligeramente en la nueva revisin de la arquitectura de formularios de Django (que est en fase de desarrollo con el nombre django.newforms). Todas estas vistas presenta formularios si se acceden con GET y realizan la operacin solicitada (crear/modicar/borrar) si se acceden con POST. Estas vistas tienen un concepto muy simple de la seguridad. Aunque aceptan un argumento llamado login_required, que restringe el acceso slo a usuarios identicados, no hacen nada ms. Por ejemplo, no comprueban que el usuario que est modicando un objeto sea el mismo usuario que lo creo, ni validarn ningn tipo de permisos. En cualquier caso, la mayor parte de las veces se puede conseguir esta funcionalidad simplemente escribiendo un pequeo recubrimiento alrededor de la vista genrica. Para ms informacin sobre esta tcnica, vase el Captulo 9.
Argumentos obligatorios model: El modelo Django del objeto a crear. Nota: Obsrvese que esta vista espera el modelo del objeto a crear, y no un QuerySet como el resto de las vistas anteriores que se han visto previamente.
Argumentos opcionales post_save_redirect: Una URL, a la cual la vista redirigir despus de haber guardado el objeto. Si no se especica, se tomar de object.get_absolute_url() post_save_redirect: puede contener cadenas de formato para diccionarios, cuyos valores se interpolarn usando los nombres de los atributos del objeto. Por ejemplo, se podra usar: post_save_redirect=/polls/ %(slug)s/".
342
login_required: Un valor booleano que obliga a que la operacin la realice un usuario identicado, ya sea para ver el formulario o para realizar la operacin de creacin del objeto. Utiliza el subsistema de autenticacin y seguridad de Django. Por defecto, su valor es False. En caso de que se dena como True, si un usuario no identicado intentase acceder a esta pgina o guardar los datos, Django le redirigira a /accounts/login/ Esta vista tambin acepta los siguientes argumentos comunes (Vase la Tabla D-1): context_processors extra_context template_loader template_name Nombre de la plantilla Si no se ha especicado ningn valor en template_name la vista usar como plantilla <app_label>/<model_name>_form.html. Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: form: Una instancia de la clase FormWrapper, que representa el formulario a utilizar. Esto te permite referirte de una forma sencilla a los campos del formulario desde la plantilla. Por ejemplo, si el modelo consta de dos atributos, name y address:
<form action="" method="post"> <p><label for="id_name">Name:</label> {{ form.name }}</p> <p><label for="id_address">Address:</label> {{ form.address }}</p> </form>
Hay que hacer notar que form es un FormWrapper denido en oldforms, y que no est contemplado en este libro. Vase http://www.djangoproject.com/documentation/0.96/forms/ par ms informacin.
343
Argumentos obligatorios model: El modelo Django a editar. Hay que prestar atencin a que es el modelo en s, y no un objeto tipo QuerySet. Y, o bien un: object_id: El valor de la clave primaria del objeto a modicar. o bien un: slug: El slug del objeto a modicar. Si se pasa este argumento, es obligatorio tambin el argumento slug_field. Argumentos opcionales slug_field: El nombre del campo en el que se almacena el valor del slug del sujeto. Es obligado usar este argumento si se ha indicado el argumento slug, pero no debe especicarse si hemos optado por identicar el objeto mediante su calve primaria, usando el argumento object_id. Esta vista acepta los mismos argumentos opcionales que la vista de creacin y, adems, el argumento comn template_object_name, explicado en la tabla D-1. Nombre de la plantilla Esta vista utiliza el mismo nombre de plantilla por defecto que la vista de creacin (<app_label>/<model_name>_form.html). Contexto de la plantilla Adems de los valores que se puedan haber denido en extra_context, el contexto de la plantilla tendr los siguientes valores: form: Una instancia de FormWrapper que representa el formulario de edicin del objeto. Vase la seccin Vista de creacin de objetos para obtener ms informacin de esta variable. object: El objeto a editar (El nombre de esta variable puede ser diferente si se ha especicado el argumento template_object_name).
344
CAPTULO 28
Tu archivo de conguracin contiene toda la conguracin de tu instalacin de Django. Este apndice explica cmo funcionan las variables de conguracin y qu variables de conguracin estn disponibles. Nota: A medida que Django crece, es ocasionalmente necesario agregar o (raramente) cambiar variables de conguracin. Debes siempre buscar la informacin mas reciente en la documentacin de conguracin en lnea que se encuentra en http://www.djangoproject.com/documentation/0.96/settings/.
Debido a que un archivo de conguracin es un mdulo Python, las siguientes armaciones son ciertas: Debe ser cdigo Python vlido; no se permiten los errores de sintaxis. El mismo puede asignar valores a las variables dinmicamente usando sintaxis normal de Python, por ejemplo:
MY_SETTING = [str(i) for i in range(30)]
Carga las variables de conguracin desde global_settings. Carga las variables de conguracin desde el archivo de conguracin especicado, plazando de ser necesario los valores globales previos. reem-
Nota que un archivo de conguracin no debe importar desde global_settings, ya que eso sera redundante.
variables
de
conguracin
importando
el
objeto
Nota que django.conf.settings no es un mdulo es un objeto. De manera que no es posible importar variables de conguracin individualmente. from django.conf.settings import DEBUG # This wont work. Ten en cuenta tambin que tu cdigo no debe importar ni desde global_settings ni desde tu propio archivo de conguracin. django.conf.settings provee abstraccin para los conceptos de variables de conguracin por omisin y variables de conguracin especcas de un sitio; presenta una nica interfaz. Tambin desacopla el cdigo que usa variables de conguracin de la ubicacin de dicha conguracin.
28.1.5 Seguridad
Debido que un archivo de conguracin contiene informacin importante, tal como la contrasea de la base de datos, debes hacer lo que est e tus manos para limitar el acceso al mismo. Por ejemplo, cambia los permisos de acceso en el sistema de archivos de manera que solo tu y el usuario de tu servidor Web puedan leerlo. Esto es especialmente importante en un entorno de alojamiento compartido.
346
Usa el argumento de lnea de comandos --settings para especicar el mdulo de conguracin en forma manual:
django-admin.py runserver --settings=mysite.settings
La utilidad manage.py creada por startproject como parte del esqueleto del proyecto asigna un valor a DJANGO_SETTINGS_MODULE en forma automtica; revisa el Apndice G si deseas conocer ms sobre manage.py.
347
<Location "/mysite/"> SetHandler python-program PythonHandler django.core.handlers.modpython SetEnv DJANGO_SETTINGS_MODULE mysite.settings </Location>
de
mod_python
en
sin
jar
DJAN-
Es algunos casos, querrs saltearte la variable de entorno DJANGO_SETTINGS_MODULE. Por ejemplo, si ests usando el sistema de plantillas en forma aislada, muy probablemente no desears tener que preparar una variable de entorno que apunte a un mdulo de conguracin. En esos casos, puedes jar los valores de las variables de conguracin de Django manualmente. Haz esto llamando a django.conf.settings.configure(). Este es un ejemplo:
from django.conf import settings settings.configure( DEBUG = True, TEMPLATE_DEBUG = True, TEMPLATE_DIRS = [ /home/web-apps/myapp , /home/web-apps/base , ] )
Psale a configure() tantos argumentos de palabra clave como desees, con cada argumento representando una variable de conguracin y su valor. Cada nombre de argumento debe estar escrito totalmente en maysculas, con el mismo nombre que que la variable de conguracin que ya se describieron. Si una variable de conguracin no es pasada a configure() y es necesario luego, Django usar el valor por omisin respectivo. El congurar Django de esta manera es en general necesario y, en efecto, recomendado cuando usas una parte del framework dentro de una aplicacin ms grande. En consecuencia, cuando es congurado va settings.configured(), Django no har modicacin alguna a las variables de entorno del proceso (revisa la explicacin acerca de TIME_ZONE mas adelante en este apndice para conocer porqu habra de ocurrir esto). Asumimos que en esos casos ya tienes completo control de tu entorno.
348
Normalmente, no necesitars sobreescribir los valores por omisin de esta manera. Los valores por omisin provistos por Django son sucientemente sensatos como para que puedas usarlos. Ten en cuenta que si pasas un nuevo valor por omisin, este reemplaza completamente los valores de Django, as que debes especicar un valor para cada variable de conguracin posible que pudiera ser usado en el cdigo que ests importando. Examina django.conf.settings.global_settings para ver la lista completa.
28.4.1 ABSOLUTE_URL_OVERRIDES
Valor por omisin: {} (diccionario vaco) Un diccionario interrelacionando cadenas app_label.model_name a funciones que toman un objeto modelo y retornan su URL. Esta es una forma de sobreescribir mtodos get_absolute_url() en cada instalacin. Un ejemplo:
ABSOLUTE_URL_OVERRIDES = { blogs.weblog : lambda o: " /blogs/ %s / " % o.slug, news.story : lambda o: " /stories/ %s / %s / " % (o.pub_year, o.slug), }
Notar que el nombre del modelo usado en esta variable de conguracin debe estar escrito totalmente en maysculas, con independencia de la combinacin de maysculas y minsculas del nombre real de la clase del modelo.
28.4.2 ADMIN_FOR
Valor por omisin: () (lista vaca) Esta variable de conguracin es usada en mdulos de conguracin de sitios de administracin. Debe ser una tupla de mdulos de conguracin (en el formato foo.bar.baz) para los cuales este sitio es una aplicacin de administracin. 28.4. Variables de conguracin disponibles 349
El sitio de administracin usa esto en la documentacin automticamente introspeccionada de modelos, vistas y etiquetas de plantillas.
28.4.3 ADMIN_MEDIA_PREFIX
Valor por omisin: /media/ Esta variable de conguracin es el prejo de la URL para los medios del sitio de administracin: CSS, JavaScript e imgenes. Asegrate de usar una barra al nal.
28.4.4 ADMINS
Valor por omisin: () (tupla vaca) Una tupla que enumera las personas que recibirn noticaciones de errores en el cdigo. Cuando DEBUG=False y una vista laza una excepcin, Django enviar a esta gente un e-mail con la informacin completa de la informacin. Cada miembro de la tupla debe ser una tupla de (Nombre completo, direccin de e-mail), por ejemplo:
(( John , john@example.com ), ( Mary , mary@example.com ))
Notar que Django el enviar e-mail a todas estas personas cada vez que ocurra un error.
28.4.5 ALLOWED_INCLUDE_ROOTS
Valor por omisin: () (tupla vaca) Una tupla de cadenas que representan prejos permitidos para la etiqueta de plantillas { % ssi %}. Se trata de una medida de seguridad, que impide que los autores de plantillas puedan acceder a archivos a los que no deberan acceder. Por ejemplo, si ALLOWED_INCLUDE_ROOTS es (/home/html, /var/www) entonces { % ssi /home/html/foo.txt %} funcionara pero { % ssi /etc/passwd %} no.
28.4.6 APPEND_SLASH
Valor por omisin: True Esta variable de conguracin indica si debe anexarse barras al nal de las URLs. Se usa solamente si est instalado CommonMiddleware (ver Captulo 15_). Ver tambin PREPEND_WWW.
28.4.7 CACHE_BACKEND
Valor por omisin: simple:// El *back-end* de cache a usarse (ver Captulo 13).
28.4.8 CACHE_MIDDLEWARE_KEY_PREFIX
Valor por omisin: (cadena vaca) El prejo de las claves de cache que debe usar el middleware de cache (ver Captulo 13).
350
28.4.9 DATABASE_ENGINE
Valor por omisin: (cadena vaca) Esta variable de conguracin indica qu *back-end* de base de datos debe usarse: postgresql_psycopg2, postgresql, mysql, mysql_old o sqlite3.
28.4.10 DATABASE_HOST
Valor por omisin: (cadena vaca) Esta variable de conguracin indica qu equipo debe usarse cuando se establezca una conexin a la base de datos. Una cadena vaca signica localhost. No se usa con SQLite. Si este valor comienza con una barra (/) y ests usando MySQL, MySQL se conectar al socket va un socket Unix:
DATABASE_HOST = /var/run/mysql
Si ests usando MySQL este valor no comienza con una barra, entonces se asume que el mismo es el nombre del equipo.
28.4.11 DATABASE_NAME
Valor por omisin: (cadena vaca) El nombre de la base de datos a usarse. Para SQLite, es la ruta completa al archivo de la base de datos.
28.4.12 DATABASE_OPTIONS
Valor por omisin: {} (diccionario vaco) Parmetros extra a usarse cuando se establece la conexin a la base de datos. Consulta el mdulo backend para conocer las palabras claves disponibles.
28.4.13 DATABASE_PASSWORD
Valor por omisin: (cadena vaca) Esta variable de conguracin es la contrasea a usarse cuando se establece una conexin a la base de datos. No se usa con SQLite.
28.4.14 DATABASE_PORT
Valor por omisin: (cadena vaca) El puerto a usarse cuando se establece una conexin a la base de datos. Una cadena vaca signica el puerto por omisin. No se usa con SQLite.
28.4.15 DATABASE_USER
Valor por omisin: (cadena vaca) Esta variable de conguracin es el nombre de usuario a usarse cuando se establece una conexin a la base da datos. No se usa con SQLite.
351
28.4.16 DATE_FORMAT
Valor por omisin: N j, Y (por ej. Feb. 4, 2003) El formato a usar por omisin para los campos de fecha en las pginas lista de cambios en el sitio de administracin de Django y, posiblemente, por otras partes del sistema. Acepta el mismo formato que la etiqueta now ver Apndice F, Tabla F-2). Ver tambin DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT y MONTH_DAY_FORMAT.
28.4.17 DATETIME_FORMAT
Valor por omisin: N j, Y, P (por ej. Feb. 4, 2003, 4 p.m.) El formato a usar por omisin para los campos de fecha-hora en las pginas lista de cambios en el sitio de administracin de Django y, posiblemente, por otras partes del sistema. Acepta el mismo formato que la etiqueta now ver Apndice F, Tabla F-2). Ver tambin DATE_FORMAT, MONTH_DAY_FORMAT. DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT y
28.4.18 DEBUG
Valor por omisin: False Esta variable de conguracin es un Booleano que activa y desactiva el modo de depuracin. Si denes variables de conguracin personalizadas, django/views/debug.py tiene una expresin regular HIDDEN_SETTINGS que ocultar de la vista DEBUG todo aquello que contenga SECRET, PASSWORD o PROFANITIES. Esto permite que usuarios en los que no se confa puedan proveer *backtraces* sin ver variables de conguracin con contenido importante (u ofensivo). Si embargo, nota que siempre existirn secciones de la salida de depuracin que son inapropiadas para el consumo del pblico. Rutas de archivos, opciones de conguracin y similares le proveen a potenciales atacantes informacin extra acerca de tu servidor. Nunca instales un sitio con DEBUG activo.
28.4.19 DEFAULT_CHARSET
Valor por omisin: utf-8 El conjunto de caracteres a usar por omisin para todos los objetos HttpResponse si no se especica en forma manual un tipo MIME. Se usa en conjunto con DEFAULT_CONTENT_TYPE para construir la cabecera Content-Type. Ver el Apndice H para ms informacin acerca de los objetos HttpResponse.
28.4.20 DEFAULT_CONTENT_TYPE
Valor por omisin: text/html Tipo de contenido a usar por omisin para todos los objetos HttpResponse, si no se especica manualmente un tipo MIME. Se usa en conjunto con DEFAULT_CHARSET para construir la cabecera Content-Type. Ver el Apndice H para conocer ms acerca de los objetos HttpResponse.
28.4.21 DEFAULT_FROM_EMAIL
Valor por omisin: webmaster@localhost La direccin de correo a usar por omisin para correspondencia automatizada enviada por el administrador del sitio. 352 Captulo 28. Apndice E: Variables de conguracin
28.4.22 DISALLOWED_USER_AGENTS
Valor por omisin: () (tupla vaca) Una lista de objetos expresiones regulares compiladas que representan cadenas User-Agent que no tiene permitido visitar ninguna pgina del sitio, a nivel global para el sitio. Usa la misma para bloquear robots y crawlers con mal comportamiento. Se usa nicamente si se ha instalado CommonMiddleware (ver Captulo 15_).
28.4.23 EMAIL_HOST
Valor por omisin: localhost El host a usarse para enviar e-mail. Ver tambin EMAIL_PORT.
28.4.24 EMAIL_HOST_PASSWORD
Valor por omisin: (cadena vaca) La contrasea a usarse para el servidor SMTP denido en EMAIL_HOST. Esta variable de conguracin se usa en combinacin con EMAIL_HOST_USER cuando se est autenticando ante el servidor SMTP. Si alguna de estas variables de conguracin est vaca, Django no intentar usar autenticacin. Ver tambin EMAIL_HOST_USER.
28.4.25 EMAIL_HOST_USER
Valor por omisin: (cadena vaca) El nombre de usuario a usarse para el servidor SMTP denido en EMAIL_HOST. Si est vaco, Django no intentar usar autenticacin. Ver tambin EMAIL_HOST_PASSWORD.
28.4.26 EMAIL_PORT
Valor por omisin: 25 El puerto a usarse pata el servidor SMTP denido en EMAIL_HOST.
28.4.27 EMAIL_SUBJECT_PREFIX
Valor por omisin: [Django] El prejo del asunto para mensajes de e-mail enviados con django.core.mail.mail_admins o django.core.mail.mail_managers. Probablemente querrs incluir un espacio al nal.
28.4.28 FIXTURE_DIRS
Valor por omisin: () (tupla vaca) Una lista de ubicaciones para los archivos con datos de *xtures*, en el orden en el que se se buscar en las mismas. Notar que esas rutas deben usar barras de estilo Unix an en Windows. Es usado por el framework de pruebas de Django, el cual se trata en http://www.djangoproject.com/documentation/0.96/testing/.
353
28.4.29 IGNORABLE_404_ENDS
Valor por omisin: (mail.pl, mailform.pl, mail.cgi, mailform.cgi, favicon.ico, .php) Ver tambin IGNORABLE_404_STARTS y Error reporting via e-mail.
28.4.30 IGNORABLE_404_STARTS
Valor por omisin: (/cgi-bin/, /_vti_bin, /_vti_inf) Una tupla de cadenas que especican las partes iniciales de URLs que deben ser ignoradas para el envo de mensajes de e-mail para errores 404. Ver tambin SEND_BROKEN_LINK_EMAILS y IGNORABLE_404_ENDS.
28.4.31 INSTALLED_APPS
Valor por omisin: () (tupla vaca) Una tupla de cadenas que indican todas las aplicaciones que estn activas en esta instalacin de Django. Cada cadena debe ser una ruta completa de Python hacia un paquete Python que contiene una aplicacin Django. Ver el Captulo 5_ para ms informacin acerca de aplicaciones.
28.4.32 INTERNAL_IPS
Valor por omisin: () (tupla vaca) Una tupla de direcciones IP, como cadenas, que: Pueden ver comentarios de depuracin cuando DEBUG es True Reciben cabeceras X si est instalado XViewMiddleware (ver Captulo 15)
28.4.33 JING_PATH
Valor por omisin: /usr/bin/jing La ruta al ejecutable Jing. Jing es un validador RELAX NG, y Django usa el mismo para validar los campos XMLField en tus modelos. Ver http://www.thaiopensource.com/relaxng/jing.html.
28.4.34 LANGUAGE_CODE
Valor por omisin: en-us Una cadena representando el cdigo de idioma para esta instalacin. Debe estar expresado usando el formato estndar por ejemplo, Ingls de EUA es en-us. Ver el Captulo 18_.
28.4.35 LANGUAGES
Valor por omisin: Una tupla de todos los idiomas disponibles. Esta lista est en continuo crecimiento y cualquier copia que incluyramos aqu inevitablemente quedara rpidamente desactualizada. Puedes ver la lista actual de idiomas traducidos examinando django/conf/global_settings.py. La lista es una tupla de tuplas de dos elementos en el formato (cdigo de idioma, nombre de idioma) por ejemplo, (ja, Japanese). Especica qu idiomas estn disponibles para la seleccin de idioma. Ver el Captulo 18_ para ms informacin acerca de seleccin de idiomas. Generalmente, el valor por omisin debera ser suciente. Solo asigna valor a esta variable de conguracin si deseas restringir la seleccin de idiomas a un subconjunto de los idiomas provistos con Django. 354 Captulo 28. Apndice E: Variables de conguracin
Si asignas un valor personalizado a LANGUAGES, est permitido marcar los idiomas como cadenas de traduccin, pero nunca debes importar django.utils.translation desde el archivo de conguracin, porque ese mdulo a su vez depende de la conguracin y esto creara una importacin circular. La solucin es usar una funcin gettext() boba. A continuacin un archivo de conguracin ejemplo:
gettext = lambda s: s LANGUAGES = ( ( de , gettext( German )), ( en , gettext( English )), )
Con este esquema, make-messages.py todava podr encontrar y marcar esas cadenas para traduccin, pero la traduccin no ocurrir en tiempo de ejecucin as que tendrs que recordar envolver los idiomas con la gettext() real en todo cdigo que use LANGUAGES en tiempo de ejecucin.
28.4.36 MANAGERS
Valor por omisin: () (tupla vaca) Esta tupla est en el mismo formato que ADMINS que especica quines deben recibir noticaciones de enlaces rotos cuando SEND_BROKEN_LINK_EMAILS tiene el valor True.
28.4.37 MEDIA_ROOT
Valor por omisin: (cadena vaca) Una ruta absoluta al directorio que contiene medios para /home/media/media.lawrence.com/"). Ver tambin MEDIA_URL. esta instalacin (por ej.
28.4.38 MEDIA_URL
Valor por omisin: (cadena vaca) Esta URL maneja los medios "http://media.lawrence.com"). servidos desde MEDIA_ROOT (por ej.
Notar que esta debe tener una barra nal si posee un componente de ruta: Correcto: "http://www.example.com/static/" Incorrecto: "http://www.example.com/static"
28.4.39 MIDDLEWARE_CLASSES
Valor por omisin:
( " django.contrib.sessions.middleware.SessionMiddleware " , " django.contrib.auth.middleware.AuthenticationMiddleware " , " django.middleware.common.CommonMiddleware " , " django.middleware.doc.XViewMiddleware " )
355
28.4.40 MONTH_DAY_FORMAT
Valor por omisin: F j El formato a usar por omisin para campos de fecha en las pginas de lista de cambios en la aplicacin de administracin de Django y, probablemente, en otras partes del sistema en casos en los que slo se muestran el mes y el da. Acepta el mismo formato que la etiqueta now (ver Apndice F, tabla F-2) Por ejemplo, cuando en una pgina de lista de cambios la aplicacin de administracin de Django se ltra por una fecha, la cabecera para un da determinado muestra el da y mes. Diferentes locales tienen diferentes formatos. Por ejemplo, el Ingls de EUA tendra January 1 mientras que Espaol podra tener 1 Enero. Ver tambin DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT y YEAR_MONTH_FORMAT.
28.4.41 PREPEND_WWW
Valor por omisin: False Esta variable de conguracin indica si se debe agregar el prejo de subdominio www. a URLs que no lo poseen. Se usa nicamente si CommonMiddleware est instalado (ver ::docCaptulo 15<chapter15>). Ver tambin APPEND_SLASH.
28.4.42 PROFANITIES_LIST
Una tupla de profanidades, como cadenas, que dispararn un error de validacin cuando se llama al validador hasNoProfanities. No enumeramos aqu los valores por omisin, debido a que esto podra hacer que nos cayera encima la comisin de clasicacin de la MPAA. Para ver los valores por omisin examina el archivo django/conf/global_settings.py.
28.4.43 ROOT_URLCONF
Valor por omisin: No denido Una cadena que representa la ruta completa de importacin Python hacia tu URLconf raz (por ej. "mydjangoapps.urls"). Ver Captulo 3.
28.4.44 SECRET_KEY
Valor por omisin: (Generado automticamente cuando creas un proyecto) Una clave secreta para esta instalacin particular de Django. Es usada para proveer una semilla para los algoritmos de hashing. Asigna un valor de una cadena con caracteres al azar mientras mas larga mejor. django-admin startproject crea una en forma automtica y en la mayora de los casos no ser necesario que la modiques.
28.4.45 SEND_BROKEN_LINK_EMAILS
Valor por omisin: False Esta variable de conguracin indica si se debe enviar un e-mail a los MANAGERS cada vez que alguien visita una pgina impulsada por Django que generar un error 404 y que posea una cabecera referer no vaca (en otras palabras un enlace roto). Es solamente usado si est instalado CommonMiddleware (ver Captulo 15). Ver tambin IGNORABLE_404_STARTS y IGNORABLE_404_ENDS.
356
28.4.46 SERIALIZATION_MODULES
Valor por omisin: No denida. La serializacin es una caracterstica que todava est bajo fuerte desarrollo. Revisa la documentacin en lnea en http://www.djangoproject.com/documentation/0.96/serialization/ si deseas ms informacin.
28.4.47 SERVER_EMAIL
Valor por omisin: root@localhost La direccin de e-mail a usarse como remitente para los mensajes de error, tales como los enviados a ADMINS y MANAGERS.
28.4.48 SESSION_COOKIE_AGE
Valor por omisin: 1209600 (dos semanas, en segundos) Esta es la edad de las cookies de sesin, en segundos. Ver Captulo 12.
28.4.49 SESSION_COOKIE_DOMAIN
Valor por omisin: None El dominio a usarse para las cookies de sesin. Asigna como valor una cadena tal como ".lawrence.com" para cookies inter-dominio, o usa None para una cookie de dominio estndar. Ver Captulo 12_.
28.4.50 SESSION_COOKIE_NAME
Valor por omisin: sessionid El nombre de la cookie a usarse para las sesiones; puede tener el valor que tu desees. Ver Captulo 12.
28.4.51 SESSION_COOKIE_SECURE
Valor por omisin: False Esta variable de conguracin indica si debe usarse una cookie segura para la cookie de sesin. Si tiene un valor True, la cookie ser marcada como segura, lo que signica que los navegadores podran asegurarse que la cookie slo se enve va una conexin HTTPS. Ver Captulo 12.
28.4.52 SESSION_EXPIRE_AT_BROWSER_CLOSE
Valor por omisin: False Esta variable de conguracin indica si las sesiones deben caducar cuando el usuario cierre su navegador. Ver Captulo 12.
28.4.53 SESSION_SAVE_EVERY_REQUEST
Valor por omisin: False Esta variable de conguracin indica si la sesin debe ser grabada en cada peticin. Ver Captulo 12.
357
28.4.54 SITE_ID
Valor por omisin: No denida. El identicador, como un entero, del sitio actual en la tabla django_site de la base de datos. Es usada de manera que datos de aplicacin puede conectarse en sitio(s) especco(s) y una nica base de datos pueda manejar contenido de mltiples sitios. Ver Captulo 14.
28.4.55 TEMPLATE_CONTEXT_PROCESSORS
Valor por omisin:
( " django.core.context_processors.auth " , " django.core.context_processors.debug " , " django.core.context_processors.i18n " )
Una tupla de *callables* que son usados para poblar el contexto en RequestContext. Esos *callables* reciben como argumento un objeto peticin y retornan un diccionario de items a ser fusionados con el contexto. Ver Captulo 10_.
28.4.56 TEMPLATE_DEBUG
Valor por omisin: False Este Booleano controla el estado encendido/apagado del modo de depuracin de plantillas. Si es True la pgina de error vistosa mostrar un reporte detallado para cada TemplateSyntaxError. Este reporte contiene los fragmentos relevantes de la plantilla, en los cuales se han resaltado las lneas relevantes. Notar que Django solo muestra pginas de error vistosas si DEBUG es True, as que es posible que desees activar dicha variable para sacar provecho de esta variable. Ver tambin DEBUG.
28.4.57 TEMPLATE_DIRS
Valor por omisin: () (tupla vaca) Un lista de ubicaciones de los archivos de cdigo fuente de plantillas, en el orden en el que sern examinadas. Notar que esas rutas deben usar barras al estilo Unix, aun en Windows. Ver Captulos 4 y 10.
28.4.58 TEMPLATE_LOADERS
Valor por omisin: (django.template.loaders.filesystem.load_template_source,) Una tupla de *callables* (como cadenas) que saben como importar plantillas desde diferentes orgenes. Ver Captulo 10.
28.4.59 TEMPLATE_STRING_IF_INVALID
Valor por omisin: (cadena vaca) La salida, como una cadena, que debe usar el sistema de plantillas para variables invlidas (por ej. con errores de sintaxis en el nombre). Ver Captulo 10.
358
28.4.60 TEST_RUNNER
Valor por omisin: django.test.simple.run_tests El nombre del mtodo a usarse para arrancar la batera de pruebas (por test suite). Es usado por el framework de pruebas de Django, el cual se describe en lnea en http://www.djangoproject.com/documentation/0.96/testing/.
28.4.61 TEST_DATABASE_NAME
Valor por omisin: None El nombre de la base de datos a usarse cuando se ejecute la batera de pruebas (por test suite). Si se especica un valor None, el nombre de la base de datos de pruebas ser test_ + settings.DATABASE_NAME. Ver la documentacin del framework de pruebas de Django el cual se describe en lnea en http://www.djangoproject.com/documentation/.
28.4.62 TIME_FORMAT
Valor por omisin: P (e.g., 4 p.m.) El formato a usar por omisin para los campos de hora en las pginas lista de cambios en el sitio de administracin de Django y, posiblemente, por otras partes del sistema. Acepta el mismo formato que la etiqueta now ver Apndice F, Tabla F-2). Ver tambin DATE_FORMAT, MONTH_DAY_FORMAT. DATETIME_FORMAT, TIME_FORMAT, YEAR_MONTH_FORMAT y
28.4.63 TIME_ZONE
Valor por omisin: America/Chicago Una cadena que representa la zona horaria para esta instalacin. Las zonas horarias se expresan en el formato zic estndar de Unix. Puede encontrarse una lista relativamente completa de cadenas de zonas horarias en http://www.postgresql.org/docs/8.1/static/datetime-keywords.html#DATETIMETIMEZONE-SET-TABLE. Esta es la zona a la cual Django convertir todas las fechas/horas no necesariamente la zona horaria del servidor. Por ejemplo, un servidor podra servir mltiples sitios impulsados por Django, cada uno con una conguracin de zona horaria separada. Normalmente, Django ja la variable os.environ[TZ] a la zona horaria que especicas en la variable de conguracin TIME_ZONE. Por lo tanto, todas tus vistas y modelos operarn automticamente en la zona horaria correcta. Sin embargo, si ests usando el mtodo de conguracin manual (descrito arriba en la seccin Usando variables de conguracin sin jar DJANGO_SETTINGS_MODULE ) Django no tocar la variable de entorno TZ y quedar en tus manos asegurarte de que tus procesos se ejecuten en el entorno correcto. Nota: Django no puede usar en forma conable zonas horarias alternativas en un entorno Windows. Si ests ejecutando Django en Windows debes asignar a esta variable un valor que coincida con la zona horaria del sistema.
359
28.4.64 URL_VALIDATOR_USER_AGENT
Valor por omisin: Django/<version> (http://www.djangoproject.com/) La cadena usarse como la cabecera User-Agent cuando se realizan vericaciones acerca e si las URLs existen (ver la opcin verify_exists de URLField; ver Apndice B).
28.4.65 USE_ETAGS
Valor por omisin: False Este Booleano especica si debe generarse la cabecera ETag. La misma permite ahorrar ancho de banda pero disminuye el rendimiento. Se usa solamente si se ha instalado CommonMiddleware (ver Captulo 15).
28.4.66 USE_I18N
Valor por omisin: True Este Booleano especica si debe activarse el sistema de internacionalizacin de Django (ver Captulo 18). Provee una forma sencilla de desactivar la internacionalizacin, para mejorar el rendimiento. Si se asigna a esta variable el valor False Django realizar algunas optimizaciones de manera que no se cargue la maquinaria de internacionalizacin.
28.4.67 YEAR_MONTH_FORMAT
Valor por omisin: F Y El formato a usar por omisin para los campos de fecha en las pginas lista de cambios en el sitio de administracin de Django y, posiblemente, por otras partes del sistema- en los casos en los que slo se muestran el mes y el ao. Acepta el mismo formato que la etiqueta now ver Apndice F). Por ejemplo, cuando se est ltrando una pgina lista de cambios de la aplicacin de administracin de Django mediante un detalle de fecha, la cabecera de un mes determinado muestra el mes y el ao. Los distintos locales tienen diferentes formatos. Por ejemplo, el Ingls de EUA usara January 2006 mientras que otro locale podra usar 2006/January. Ver tambin DATE_FORMAT, DATETIME_FORMAT, TIME_FORMAT y MONTH_DAY_FORMAT.
360
CAPTULO 29
En el Captulo 4 se hace una introduccin de las etiquetas de plantilla y ltros ms utilizados, pero Django incorpora muchas ms. En este apndice se listan todas las que estaban incluidas en el momento en que se escribi el libro, pero se aaden nuevas etiquetas y ltros de forma regular. La mejor referencia de todas las etiquetas y ltros disponibles se encuentra en la propia pgina de administracin. All se incluye una referencia completa de todas las etiquetas y ltros que hay disponibles para una determinada aplicacin. Para verla, slo tienes que pulsar con el ratn en el enlace de documentacin que est en la esquina superior derecha de la pgina. Las secciones de etiquetas y ltros de esta documentacin incluirn tanto las etiquetas y ltros predenidos (de hecho, las referencias de este apndice vienen directamente de ah) como aquellas etiquetas y ltros que se hayan incluido o escrito para la aplicacin. Este apndice ser ms til, por tanto, para aquellos que no dispongan de acceso a la interfaz de administracin. Como Django es altamente congurable, las indicaciones de la interfaz de administracin deben ser consideradas como la documentacin ms actualizada y, por tanto, la de mayor autoridad.
29.1.2 comment
Ignora todo lo que aparezca entre { % comment %} y { % endcomment %}.
29.1.3 cycle
Rota una cadena de texto entre diferentes valores, cada vez que aparece la etiqueta. Dentro de un bucle, el valor rotan entre los distintos valores disponibles en cada iteracin del bucle:
361
{ % for o in some_list %} <tr class="{ % cycle row1,row2 %}"> ... </tr> { % endfor %}
Fuera de un bucle, hay que asignar un nombre nico la primera vez que se usa la etiqueta, y luego hay que incluirlo ese nombre en las sucesivas llamadas:
<tr class="{ % cycle row1,row2,row3 as rowcolors %}">...</tr> <tr class="{ % cycle rowcolors %}">...</tr> <tr class="{ % cycle rowcolors %}">...</tr>
Se pueden usar cualquier nmero de valores, separndolos por comas. Asegrate de no poner espacios entre los valores, slo comas.
29.1.4 debug
Muestra un montn de informacin para depuracin de errores, incluyendo el contexto actual y los mdulos importados.
29.1.5 extends
Sirve para indicar que esta plantilla extiende una plantilla padre. Esta etiqueta se puede usar de dos maneras: { % extends "base.html" %} (Con las comillas) interpreta literalmente "base.html" como el nombre de la plantilla a extender. { % extends variable %} usa el valor de variable. Si la variable apunta a una cadena de texto, Django usar dicha cadena como el nombre de la plantilla padre. Si la variable es un objeto de tipo Template, se usar ese mismo objeto como plantilla base. En el Captulo 4 podrs encontrar muchos ejemplo de uso de esta etiqueta.
29.1.6 lter
Filtra el contenido de una variable. Los ltros pueden ser encadenados sucesivamente (La salida de uno es la entrada del siguiente), y pueden tener argumentos, como en la sintaxis para variables He aqu un ejemplo:
{ % filter escape|lower %} This text will be HTML-escaped, and will appear in all lowercase. { % endfilter %}
29.1.7 rstof
Presenta como salida la primera de las variables que se le pasen que evale como no falsa. La salida ser nula si todas las variables pasadas valen False. He aqu un ejemplo:
{ % firstof var1 var2 var3 %}
362
Equivale a:
{ % if var1 %} {{ var1 }} { % else %}{ % if var2 %} {{ var2 }} { % else %}{ % if var3 %} {{ var3 }} { % endif %}{ % endif %}{ % endif %}
29.1.8 for
Itera sobre cada uno de los elementos de un lista o array. Por ejemplo, para mostrar una lista de atletas, cuyos nombres estn en la lista athlete_list, podramos hacer:
<ul> { % for athlete in athlete_list %} <li>{{ athlete.name }}</li> { % endfor %} </ul>
Tambin se puede iterar la lista en orden inverso usando { % for obj in list reversed %}. Dentro de un bucle, la propia sentencia for crea una serie de variables. A estas variables se puede acceder nicamente dentro del bucle. Las distintas variables se explican en la Tabla F-1. Variable forloop.counter forloop.counter0 forloop.revcounter forloop.revcounter0 forloop.first forloop.last forloop.parentloop Descripcin El nmero de vuelta o iteracin actual (usando un ndice basado en 1). El nmero de vuelta o iteracin actual (usando un ndice basado en 0). El nmero de vuelta o iteracin contando desde el n del bucle (usando un ndice basado en 1). El nmero de vuelta o iteracin contando desde el n del bucle (usando un ndice basado en 0). True si es la primera iteracin. True si es la ltima iteracin. Para bucles anidados, es una referencia al bucle externo.
29.1.9 if
La etiqueta { % if %} evalua una variable. Si dicha variable se evalua como una expresin verdadera (Es decir, que el valor exista, no est vacia y no es el valor booleano False), se muestra el contenido del bloque:
{ % if athlete_list %} Number of athletes: {{ athlete_list|length }} { % else %} No athletes. { % endif %}
Si la lista athlete_list no est vaca, podemos mostrar el nmero de atletas con la expresin {{ athlete_list|length }} Adems, como se puede ver en el ejemplo, la etiqueta if puede tener un bloque opcional { % else %} que se mostrar en el caso de que la evaluacin de falso.
363
Las etiquetas if pueden usar operadores lgicos como and, or y not para evaluar expresiones ms complejas:
{ % if athlete_list and coach_list %} Both athletes and coaches are available. { % endif %} { % if not athlete_list %} There are no athletes. { % endif %} { % if athlete_list or coach_list %} There are some athletes or some coaches. { % endif %} { % if not athlete_list or coach_list %} There are no athletes or there are some coaches (OK, so writing English translations of Boolean logic sounds stupid; its not our fault). { % endif %} { % if athlete_list and not coach_list %} There are some athletes and absolutely no coaches. { % endif %}
La etiqueta if no admite, sin embargo, mezclar los operadores and y or dentro de la misma comprobacin, porque la orden de aplicacin de los operadores lgicos sera ambigua. Por ejemplo, el siguiente cdigo es invlido:
{ % if athlete_list and coach_list or cheerleader_list %}
Para combinar operadores and y or, puedes usar sentencias if anidadas, como en el siguiente ejemplo:
{ % if athlete_list %} { % if coach_list or cheerleader_list %} We have athletes, and either coaches or cheerleaders! { % endif %} { % endif %}
Es perfectamente posible usar varias veces un operador lgico, siempre que sea el mismo siempre. Por ejemplo, el siguiente cdigo es vlido:
{ % if athlete_list or coach_list or parent_list or teacher_list %}
29.1.10 ifchanged
Comprueba si un valor ha sido cambiado desde la ltima iteracin de un bucle. La etiqueta ifchanged solo tiene sentido dentro de un bucle. Tiene dos usos posibles: 1. Comprueba su propio contenido mostrado contra su estado anterior, y solo lo muestra si el contenido ha cambiado. El siguiente ejemplo muestra una lista de das, y solo aparecer el nombre del mes si este cambia:
<h1>Archive for {{ year }}</h1> { % for date in days %} { % ifchanged %}<h3>{{ date|date:"F" }}</h3>{ % endifchanged %} <a href="{{ date|date:"M/d"|lower }}/">{{ date|date:"j" }}</a> { % endfor %}
364
{ % for date in days %} { % ifchanged date.date %} {{ date.date }} { % endifchanged %} { % ifchanged date.hour date.date %} {{ date.hour }} { % endifchanged %} { % endfor %}
El ejemplo anterior muestra la fecha cada vez que cambia, pero slo muestra la hora si tanto la hora como el da han cambiado.
29.1.11 ifequal
Muestra el contenido del bloque si los dos argumentos suministrados son iguales. He aqu un ejemplo:
{ % ifequal user.id comment.user_id %} ... { % endifequal %}
Al igual que con la etiqueta { % if %}, existe una clusula { % else %} opcional. Los argumentos pueden ser cadenas de texto, as que el siguiente cdigo es vlido:
{ % ifequal user.username "adrian" %} ... { % endifequal %}
Slo se puede comprobar la igualdad de variables o cadenas de texto. No se puede comparar con objetos Python como True o False. Para ello, utilice la etiqueta if directamente.
29.1.12 ifnotequal
Es igual que ifequal, excepto que comprueba que los dos parmetros suministrados no sean iguales.
29.1.13 include
Carga una plantilla y la representa usando el contexto actual. Es una forma de incluir una plantilla dentro de otra. El nombre de la plantilla puede o bien ser el valor de una variable o estar escrita en forma de cadena de texto, rodeada ya sea con comillas simples o comillas dobles, a gusto del lector. El siguiente ejemplo incluye el contenido de la plantilla "foo/bar.html":
{ % include "foo/bar.html" %}
Este otro ejemplo incluye el contenido de la plantilla cuyo nombre sea el valor de la variable template_name:
{ % include template_name %}
29.1.14 load
Carga una biblioteca de plantillas. En el Captulo 10_ puedes encontrar ms informacin acerca de las bibliotecas de plantillas.
365
29.1.15 now
Muestra la fecha, escrita de acuerdo a un formato indicado. Esta etiqueta fue inspirada por la funcin date() de PHP(), y utiliza el mismo formato que esta (http://php.net/date). La versin Django tiene, sin embargo, algunos extras. La tabla F-2 muestra las cadenas de formato que se pueden utilizar. Carc. formato a A b d D f F g G h H i j l L m M n N O P r s S t T w W y Y z Z He aqu un ejemplo:
It is { % now "jS F Y H:i" %}
Descripcin a.m. o p.m.. (Obsrvese que la salida es ligeramente distinta de la de PHP, ya que aqu se in AM o PM. El nombre del mes, en forma de abreviatura de tres letras minsculas. Da del mes, dos dgitos que incluyen rellenando con cero por la izquierda si fuera necesario. Da de la semana, en forma de abreviatura de tres letras. La hora, en formato de 12 horas y minutos, omitiendo los minutos si estos son cero. El mes, en forma de texto La hora, en formato de 12 horas, sin rellenar por la izquierda con ceros. La hora, en formato de 24 horas, sin rellenar por la izquierda con ceros. La hora, en formato de 12 horas. La hora, en formato de 24 horas. Minutos. El da del mes, sin rellenar por la izquierda con ceros. El nombre del da de la semana. Booleano que indica si el ao es bisiesto. El da del mes, rellenando por la izquierda con ceros si fuera necesario. Nombre del mes, abreviado en forma de abreviatura de tres letras. El mes, sin rellenar con ceros La abreviatura del mes siguiendo el estilo de la Associated Press. Diferencia con respecto al tiempo medio de Grennwich (Greenwich Mean Time - GMT) La hora, en formato de 12 horas, ms los minutos, recto si estos son cero y con la indicacin a.m./p La fecha en formato RFC 822. Los segundos, rellenos con ceros por la izquierda de ser necesario. El sujo ingls para el da del mes (dos caracteres). Nmero de das del mes. Zona horaria Da de la semana, en forma de dgito. Semana del ao, siguiente la norma ISO-8601, con la semana empezando el lunes. Ao, con dos dgitos. Ao, con cuatro dgitos. Da del ao Desfase de la zona horaria, en segundos. El desplazamiento siempre es negativo para las zonas al o
Se pueden escapar los caracteres de formato con una barra invertida, si se quieren incluir de forma literal. En el siguiente ejemplo, se escapa el signicado de la letra f con la barra invertida, ya que de otra manera se interpretara como una indicacin de incluir la hora. La o, por otro lado, no necesita ser escapada, ya que no es un carcter de formato:
It is the { % now "jS o\f F" %}
366
29.1.16 regroup
Reagrupa una lista de objetos similares usando un atributo comn. Para comprender esta etiqueta, es mejor recurrir a un ejemplo. Digamos que people es una lista de objetos de tipo Person, y que dichos objetos tienen los atributos first_name, last_name y gender. Queremos mostrar un listado como el siguiente:
* Male:
George Bush Bill Clinton Female: Margaret Thatcher Condoleezza Rice Unknown: Pat Smith El siguiente fragmento de plantilla mostrara como realizar esta tarea:
{ % regroup people by gender as grouped %} <ul> { % for group in grouped %} <li>{{ group.grouper }} <ul> { % for item in group.list %} <li>{{ item }}</li> { % endfor %} </ul> </li> { % endfor %} </ul>
Como puedes ver, { % regroup %} crea una nueva variable, que es una lista de objetos que tienen dos tributos, grouper y list. En grouper se almacena el valor de agrupacin, list contiene una lista de los objetos que tenan en comn al valor de agrupacin. En este caso, grouper podra valer Male, Female y Unknown, y list sera una lista con las personas correspondientes a cada uno de estos sexos. Hay que destacar que { % regroup %} no funciona correctamente cuando la lista no est ordenada por el mismo atributo que se quiere agrupar. Esto signica que si la lista del ejemplo no est ordenada por el sexo, debes asegurarte de que se ordene antes correctamente, por ejemplo con el siguiente cdigo:
{ % regroup people|dictsort:"gender" by gender as grouped %}
29.1.17 spaceless
Elimina los espacios en blanco entre etiquetas Html. Esto incluye tabuladores y saltos de lnea. El siguiente ejemplo:
{ % spaceless %} <p> <a href="foo/">Foo</a> </p> { % endspaceless %}
367
Slo se eliminan los espacios entre las etiquetas, no los espacios entre la etiqueta y el texto. En el siguiente ejemplo, no se quitan los espacios que rodean la palabra Hello:
{ % spaceless %} <strong> Hello </strong> { % endspaceless %}
29.1.18 ssi
Muestra el contenido de un chero determinado dentro de la pgina. Al igual que la etiqueta include, { % ssi %} incluye el contenido de otro chero (que debe ser especicado usando una ruta absoluta) en la pgina actual:
{ % ssi /home/html/ljworld.com/includes/right_generic.html %}
Si se le pasa el parmetro opcional parsed, el contenido del chero incluido se evala como si fuera cdigo de plantilla, usando el contexto actual:
{ % ssi /home/html/ljworld.com/includes/right_generic.html parsed %}
Para poder usar la etiqueta { % ssi %}, hay que denir el valor ALLOWED_INCLUDE_ROOTS en los ajustes de Django, como medida de seguridad. La mayor parte de las veces, { % include %} funcionar mejor que { % ssi %}; esta se ha incluido slo para garantizar compatibilidad hacia atrs.
29.1.19 templatetag
Permite representar los caracteres que estn denidos como parte del sistema de plantillas. Como el sistema de plantillas no tiene el concepto de escapar el signicado de las combinaciones de smbolos que usa internamente, tenemos que recurrir a la etiqueta { % templatetag %} si nos vemos obligados a representarlos. Se le pasa un argumento que indica que combinacin de smbolos debe producir. Los valores posibles del argumento se muestran en la tabla F-3. Argumento openblock closeblock openvariable closevariable openbrace closebrace opencomment closecomment Salida {% %} {{ }} { } {# #}
29.1.20 url
Devuelve una URL absoluta (Es decir, una URL sin la parte del dominio) que coincide con una determinada vista, incluyendo sus parmetros opcionales. De esta forma se posibilita realizar enlaces sin violar el principio DRY, codicando las direcciones en nuestras plantillas:
{ % url path.to.some_view arg1,arg2,name1=value1 %}
368
El primer argumento es la ruta a la funcin de vista, en el formato paquete.paquete.modulo.funcion. El resto de parmetros son opcionales y deben ir separados con comas, convirtindose en parmetros posicionales o por nombre que se incluirn en la URL. Deben estar presentes todos los argumentos que se hayan denido como obligatorios en el URLconf. Por ejemplo, supongamos que tenemos una vista, app_name.client, y que en el URLconf se la indica que acepta un parmetro, el identicador del cliente. La lnea del URL podra ser algo as:
( ^client/( \ d+)/$ , app_name.client )
Si este URLconf fuera incluido en el URLconf del proyecto bajo un directorio, como en este ejemplo:
( ^clients/ , include( project_name.app_name.urls ))
Podramos crear un enlace a esta vista, en nuestra plantilla, con la siguiente etiqueta:
{ % url app_name.client client.id %}
29.1.21 widthratio
Esta etiqueta es til para presentar grcos de barras y similares. Calcula la proporcin entre un valor dado y un mximo predenido, y luego multiplica ese cociente por una constante. Veamos un ejemplo:
<img src="bar.gif" height="10" width="{ % widthratio this_value max_value 100 %}" />
Si this_value vale 175 y max_value es 200, la imagen resultante tendr un ancho de 88 pixels (porque 175/200 = 0.875 y 0.875 * 100 = 87.5, que se redondea a 88).
29.2.2 addslashes
Ejemplo:
{{ string|addslashes }}
Aade barras invertidas antes de las comillas, ya sean simples o dobles. Es til para pasar cadenas de texto como javascript, por ejemplo:
29.2.3 caprst
Ejemplo:
{{ string|capfirst }}
Pasa a maysculas la primera letra de la primera palabra. 29.2. Filtros predenidos 369
29.2.4 center
Ejemplo:
{{ string|center:"50" }}
29.2.5 cut
Ejemplo:
{{ string|cut:"spam" }}
29.2.6 date
Ejemplo:
{{ value|date:"F j, Y" }}
Formatea una fecha de acuerdo al formato indicado en la cadena de texto (Se usa el mismo formato que con la etiqueta now).
29.2.7 default
Ejemplo:
{{ value|default:"(N/A)" }}
29.2.8 default_if_none
Ejemplo:
{{ value|default_if_none:"(N/A)" }}
29.2.9 dictsort
Ejemplo:
{{ list|dictsort:"foo" }}
Acepta una lista de diccionarios y devuelve una lista ordenada segn la propiedad indicada en el argumento.
29.2.10 dictsortreversed
Ejemplo:
{{ list|dictsortreversed:"foo" }}
370
Acepta una lista de diccionarios y devuelve una lista ordenada de forma descendente segn la propiedad indicada en el argumento.
29.2.11 divisibleby
Ejemplo:
{ % if value|divisibleby:"2" %} Even! { % else %} Odd! { % else %}
29.2.12 escape
Ejemplo:
{{ string|escape }}
Transforma un texto que est en HTML de forma que se pueda representar en una pgina web. Concretamente, realiza los siguientes cambios: "&" a "&" < a "<" > a ">" (comilla doble) a " " (comillas simple) a '
29.2.13 lesizeformat
Ejemplo:
{{ value|filesizeformat }}
Representa un valor, interpretndolo como si fuera el tamao de un chero y humanizando el resultado, de forma que sea fcil de leer. Por ejemplo, las salidas podran ser 13 KB, 4.1 MB, 102 bytes, etc.
29.2.14 rst
Ejemplo:
{{ list|first }}
29.2.15 x_ampersands
Ejemplo:
{{ string|fix_ampersands }}
Reemplaza los smbolos ampersand con la entidad &. 29.2. Filtros predenidos 371
29.2.16 oatformat
Ejemplos:
{{ value|floatformat }} {{ value|floatformat:"2" }}
Si se usa sin argumento, redondea un nmero en coma otante a un nico dgito decimal (pero slo si hay una parte decimal que mostrar), por ejemplo: 36.123 se representara como 36.1. 36.15 se representara como 36.2. 36 se representara como 36. Si te utiliza un argumento numrico, floatformat redondea a ese nmero de lugares decimales: 36.1234 con oatformat:3 se representara como 36.123. 36 con oatformat:4 se representara como 36.0000. Si el argumento pasado a floatformat es negativo, redondear a ese nmero de decimales, pero slo si el nmero tiene parte decimal. 36.1234 con oatformat:-3 gets converted to 36.123. 36 con oatformat:-4 gets converted to 36. Usar floatformat sin argumentos es equivalente a usarlo con un argumento de -1.
29.2.17 get_digit
Ejemplo:
{{ value|get_digit:"1" }}
Dado un nmero, devuelve el dgito que est en la posicin indicada, siendo 1 el dgito ms a la derecha. En caso de que la entrada sea invlida, devolver el valor original (Si la entrada o el argumento no fueran enteros, o si el argumento fuera inferior a 1). Si la entrada es correcta, la salida siempre ser un entero.
29.2.18 join
Ejemplo:
{{ list|join:", " }}
Concatena todos los elementos de una lista para formar una cadena de texto, usando como separador el texto que se le pasa como argumento. Es equivalente a la llamada en Python str.join(list)
29.2.19 length
Ejemplo:
{{ list|length }}
372
29.2.20 length_is
Ejemplo:
{ % if list|length_is:"3" %} ... { % endif %}
Devuelve un valor booleano que ser verdadero si la longitud de la entrada coincide con el argumento suministrado.
29.2.21 linebreaks
Ejemplo:
{{ string|linebreaks }}
29.2.22 linebreaksbr
Ejemplo:
{{ string|linebreaksbr }}
29.2.23 linenumbers
Ejemplo:
{{ string|linenumbers }}
29.2.24 ljust
Ejemplo:
{{ string|ljust:"50" }}
29.2.25 lower
Ejemplo:
{{ string|lower }}
29.2.26 make_list
Ejemplo:
373
Devuelve la entrada en forma de lista. Si la entrada es un nmero entero, se devuelve una lista de dgitos. Si es una cadena de texto, se devuelve una lista de caracteres.
29.2.27 phone2numeric
Ejemplo:
{{ string|phone2numeric }}
Convierte un nmero de telfono (que incluso puede contener letras) a su forma numrica equivalente. Por ejemplo 800-COLLECT se transformar en 800-2655328. La entrada no tiene porque ser un nmero de telfono vlido. El ltro convertir alegremente cualquier texto que se le pase.
29.2.28 pluralize
Ejemplo:
The list has {{ list|length }} item{{ list|pluralize }}.
Retorno el sujo para formar el plural si el valor es mayor que uno. Por defecto el sujo es s. Ejemplo:
You have {{ num_messages }} message{{ num_messages|pluralize }}.
Para aquellas palabras que requieran otro sujo para formar el plural, podemos usar una sintaxis alternativa en la que indicamos el sujo que queramos con un argumento. Ejemplo:
You have {{ num_walruses }} walrus{{ num_walrus|pluralize:"es" }}.
Para aquellas palabras que forman el plural de forma ms compleja que con un simple sujo, hay otra tercera sintaxis que permite indicar las formas en singular y en plural a partir de una raz comn. Ejemplo:
You have {{ num_cherries }} cherr{{ num_cherries|pluralize:"y,ies" }}.
29.2.29 pprint
Ejemplo:
{{ object|pprint }}
Un recubrimiento que permite llamar a la funcin de Python pprint.pprint. Se usa sobre todo para tareas de depurado de errores.
29.2.30 random
Ejemplo:
374
{{ list|random }}
29.2.31 removetags
Ejemplo:
{{ string|removetags:"br p div" }}
Elimina de la entrada una o varias clases de etiquetas [X]HTML. Las etiquetas se indican en forma de texto, separando cada etiqueta a eliminar por un espacio.
29.2.32 rjust
Ejemplo:
{{ string|rjust:"50" }}
29.2.33 slice
Ejemplo:
{{ some_list|slice:":2" }}
Devuelve una seccin de la lista. Usa la misma sintaxis que se usa en Python para seccionar una lista. Vase http://diveintopython.org/native_data_types/lists.html#odbchelper.list.slice para una explicacin.
29.2.34 slugify
Ejemplo:
{{ string|slugify }}
Convierte el texto a minsculas, elimina los caracteres que no formen palabras (caracteres alfanumricos y carcter subrayado), y convierte los espacios en guiones. Tambin elimina los espacios que hubiera al principio y al nal del texto.
29.2.35 stringformat
Ejemplo:
{{ number|stringformat:"02i" }}
Formatea el valor de entrada de acuerdo a lo especicado en el formato que se le pasa como parmetro. La sintaxis a utilizar es idntica a la de Python, con la excepcin de que el carcter % se omite. En http://docs.python.org/lib/typesseq-strings.html puedes consultar las opciones de formateo de cadenas de Python.
375
29.2.36 striptags
Ejemplo:
{{ string|striptags }}
29.2.37 time
Ejemplo:
{{ value|time:"P" }}
Formatea la salida asumiendo que es una fecha/hora, con el formato indicado como argumento (Lo mismo que la etiqueta now).
29.2.38 timesince
Ejemplos:
{{ datetime|timesince }} {{ datetime|timesince:"other_datetime" }}
Representa una fecha como un intervalo de tiempo (por ejemplo, 4 days, 6 hours). Acepta un argumento opcional, que es una variable con la fecha a usar como punto de referencia para calcular el intervalo (Si no se especica, la referencia es el momento actual). Por ejemplo, si blog_date es una fecha con valor igual a la medianoche del 1 de junio de 2006, y comment_date es una fecha con valor las 08:00 horas del da 1 de junio de 2006, entonces {{ comment_date|timesince:blog_date }} devolvera 8 hours.
29.2.39 timeuntil
Ejemplos:
{{ datetime|timeuntil }} {{ datetime|timeuntil:"other_datetime" }}
Es similar a timesince, excepto en que mide el tiempo desde la fecha de referencia hasta la fecha dada. Por ejemplo, si hoy es 1 de junio de 2006 y conference_date es una fecha cuyo valor es igual al 29 de junio de 2006, entonces {{ conference_date|timeuntil }} devolvera 28 days. Acepta un argumento opcional, que es una variable con la fecha a usar como punto de referencia para calcular el intervalo, si se quiere usar otra distinta del momento actual. Si from_date apunta al 22 de junio de 2006, entonces {{ conference_date|timeuntil:from_date }} devolvera 7 days.
29.2.40 title
Ejemplo:
{{ string|titlecase }}
Representa una cadena de texto en forma de ttulo, siguiendo las convenciones del idioma ingls (todas las palabras con la inicial en mayscula).
376
29.2.41 truncatewords
Ejemplo:
{{ string|truncatewords:"15" }}
Recorta la salida de forma que tenga como mximo el nmero de palabras que se indican en el argumento.
29.2.42 truncatewords_html
Ejemplo:
{{ string|truncatewords_html:"15" }}
Es similar a truncatewords, excepto que es capaz de reconocer las etiquetas HTML y, por tanto, no deja etiquetas hurfanas. Cualquier etiqueta que se hubiera abierto antes del punto de recorte es cerrada por el propio ltro. Es menos eciente que truncatewords, as que debe ser usada solamente si sabemos que en la entrada va texto HTML.
29.2.43 unordered_list
Ejemplo:
<ul> {{ list|unordered_list }} </ul>
Acepta una lista, e incluso varias listas anidadas, y recorre recursivamente las mismas representndolas en forma de listas HTML no ordenadas, sin incluir las etiquetas de inicio y n de lista (<ul> y </ul> respectivamente). Se asume que las listas est en el formato correcto. Por ejemplo, si var contiene [States, [[Kansas, [[Lawrence, []], [Topeka, []]]], [Illinois, []]]], entonces {{ var|unordered_list }} retornara lo siguiente:
<li>States <ul> <li>Kansas <ul> <li>Lawrence</li> <li>Topeka</li> </ul> </li> <li>Illinois</li> </ul> </li>
29.2.44 upper
Ejemplo:
{{ string|upper }}
377
29.2.45 urlencode
Ejemplo:
<a href="{{ link|urlencode }}">linkage</a>
Escapa la entrada de forma que pueda ser utilizado dentro de una URL.
29.2.46 urlize
Ejemplo:
{{ string|urlize }}
Transforma un texto de entrada, de forma que si contiene direcciones URL en texto plano, las convierte en enlaces HTML.
29.2.47 urlizetrunc
Ejemplo:
{{ string|urlizetrunc:"30" }}
Convierte las direcciones URL de un texto en enlaces, recortando la representacin de la URL para que el nmero de caracteres sea como mximo el del argumento suministrado.
29.2.48 wordcount
Ejemplo:
{{ string|wordcount }}
29.2.49 wordwrap
Ejemplo:
{{ string|wordwrap:"75" }}
Ajusta la longitud del texto para las lneas se adecen a la longitud especicada como argumento.
29.2.50 yesno
Ejemplo:
{{ boolean|yesno:"Yes,No,Perhaps" }}
Dada una serie de textos que se asocian a los valores de True, False y (opcionalmente) None, devuelve uno de esos textos segn el valor de la entrada. Vase la tabla F-4.
2 2 2 2
Salida yeah no maybe "no" (considera None como False si no se asigna ningn texto a None.
378
CAPTULO 30
django-admin.py es el utilitario de linea de comandos de Django para tareas administrativas. Este apndice explica sus mltiples poderes. Usualmente accedes a django-admin.py a travs del wrapper del proyecto manage.py. manage.py es creado automticamente en cada proyecto Django y es un wrapper liviano en torno a django-admin.py. Toma cuidado de dos cosas por ti antes de delegar a django-admin.py: Pone el paquete de tu proyecto en sys.path. Establece la variable de entorno DJANGO_SETTINGS_MODULE para que apunte al archivo settings.py de tu proyecto. El script django-admin.py debe estar en la ruta de tu sistema si instalaste Django mediante su utilitario setup.py. Si no est en tu ruta, puedes encontrarlo en site-packages/django/bin dentro de tu instalacin de Python. Considera establecer un enlace simblico a l desde algn lugar en tu ruta, como en /usr/local/bin. Los usuarios de Windows, que no disponen de la funcionalidad de los enlaces simblicos, pueden copiar django-admin.py a una ubicacin que est en su ruta existente o editar la conguracin del PATH (bajo Conguracin ~TRA Panel de Control ~TRA Sistema ~TRA Avanzado ~TRA Entorno) para apuntar a la ubicacin de su instalacin. Generalmente, cuando se trabaja en un proyecto Django simple, es ms fcil usar manage.py. Usa django-admin.py con DJANGO_SETTINGS_MODULE o la opcin de lnea de comando --settings, si necesitas cambiar entre mltiples archivos de conguracin de Django. Los ejemplos de lnea de comandos a lo largo de este apndice usan django-admin.py para ser consistentes, pero cada ejemplo puede usar de la misma forma manage.py.
30.1 Uso
El uso bsico es:
django-admin.py action [options]
o:
manage.py action [options]
action debe ser una de las acciones listadas en este documento. options, que es opcional, deben ser cero o ms opciones de las listadas en este documento. Ejecuta django-admin.py --help para ver un mensaje de ayuda que incluye una larga lista de todas las opciones y acciones disponibles. 379
La mayora de las acciones toman una lista de nombres de aplicacin. Un nombre de aplicacin es el nombre base del paquete que contiene tus modelos. Por ejemplo, si tu INSTALLED_APPS contiene el string mysite.blog, el nombre de la aplicacin es blog.
30.2.3 dbshell
Corre el cliente de lnea de comandos del motor de base de datos especicado en tu conguracin de DATABASE_ENGINE, con los parmetros de conexin especicados en la conguracin de DATABASE_USER, DATABASE_PASSWORD, etc. Para PostgreSQL, esto ejecuta el cliente de lnea de comandos psql . For MySQL, esto ejecuta el cliente de lnea de comandos mysql. For SQLite, esto ejecuta el cliente de lnea de comandos sqlite3. Este comando asume que los programas estn en tu PATH de manera que una simple llamada con el nombre del programa (psql, mysql, o sqlite3) encontrar el programa en el lugar correcto. No hay forma de especicar en forma manual la localizacin del programa.
30.2.4 diffsettings
Muestra las diferencias entre la conguracin actual y la conguracin por omisin de Django. Las conguraciones que no aparecen en la conguracin por omisin estn seguidos por "###". Por ejemplo, la conguracin por omisin no dene ROOT_URLCONF, por lo que si aparece ROOT_URLCONF en la salida de diffsettings lo hace seguido de "###". Observa que la conguracin por omisin de Django habita en django.conf.global_settings, si alguna vez sientes curiosidad por ver la lista completa de valores por omisin.
Si no se provee niingn nombre de aplicacin, se volcarn todas las aplicaciones instaladas. La salida de dumpdata puede usarse como entrada para loaddata.
30.2.6 ush
Devuelve la base de datos al estado en el que estaba inmediatemente despus de que se ejecut syncdb. Esto signica que todos los datos sern eliminados de la base de datos, todo manejador de postsinchronizacin ser reejecutado, y el componente initial_data ser reinstalado.
30.2.7 inspectdb
Realiza la introspeccin sobre las tablas de la base de datos apuntada por la conguracin DATABASE_NAME y enva un modulo de modelo de Django (un archivo models.py) a la salida estndar. Usa esto si tienes una base de datos personalizada con la cual quieres usar Django. El script inspeccionar la base de datos y crear un modelo para cada tabla que contenga. Como podrs esperar, los modelos creados tendrn un atributo por cada campo de la tabla. Observa que inspectdb tiene algunos casos especiales en los nombres de campo resultantes: Si inspectdb no puede mapear un tipo de columna a un tipo de campo del modelo, usar TextField e insertar el comentario Python This field type is a guess. junto al campo en el modelo generado. Si el nombre de columna de la base de datos es una palabra reservada de Python( como pass, class, o for), inspectdb agregar _field al nombre de atributo. Por ejemplo, si una tabla tiene una columna for, el modelo generado tendr un campo for_field, con el atributo db_column establecido en for. inspectdb insertar el comentario Python Field renamed because it was a Python reserved word. junto al campo. Esta caracterstica est pensada como un atajo, no como la generacin de un modelo denitivo. Despus de ejecutarla, querrs revisar los modelos genrados para personalizarlos. En particular, necesitars reordenar los modelos de manera tal que las relaciones esten ordenadas adecuadamente. Las claves primarias son detectadas automticamente durante la introspeccin para PostgreSQL, MySQL, y SQLite, en cuyo caso Django coloca primary_key=True donde sea necesario. inspectdb trabaja con PostgreSQL, MySQL, y SQLite. La deteccin de claves forneas solo funciona en PostgreSQL y con ciertos tipos de tablas MySQL.
381
slo cargar xtures JSON llamados mydata. La extensin del xture debe corresponder al nombre registrado de un serializador (ej.: json o xml). Si omites la extensin, Django buscar todos los tipos de xture disponibles para un xture coincidente. Por ejemplo, lo siguiente:
django-admin.py loaddata mydata
buscar todos los xture de cualquier tipo de xture llamado mydata. Si un directorio de xture contiene mydata.json, ese xture ser cargado como un xture JSON. De todas formas, si se descubren dos xtures con el mismo nombre pero diferente tipo (ej.: si se encuentran mydata.json y mydata.xml en el mismo directorio de xture), la instalacin de xture ser abortada, y todo dato instalado en la llamada a loaddata ser removido de la base de datos. Los xtures que son nombrados pueden incluir como componentes directorios. Estos directorios sern incluidos en la ruta de bsqueda. Por ejemplo, lo siguiente:
django-admin.py loaddata foo/bar/mydata.json
buscar <appname>/fixtures/foo/bar/mydata.json para cada aplicacin instalada, <dirname>/foo/bar/mydata.json para cada directorio en FIXTURE_DIRS, y la ruta literal foo/bar/mydata.json. Observa que el orden en que cada xture es procesado es indenido. De todas formas, todos los datos de xture son instalados en una nica transaccin, por lo que los datos en un xture pueden referenciar datos en otro xture. Si el back-end de la base de datos admite restricciones a nivel de registro, estas restricciones sern chequeadas al nal de la transaccin. El comando dumpdata puede ser usado para generar la entrada para loaddata. MySQL y los Fixtures Desafortunadamente, MySQL no es capaz de dar soporte completo para todas las caractersticas de las xtures de Django. Si usas tablas MyISAM, MySQL no admite transacciones ni restricciones, por lo que no tendrs rollback si se encuentran varios archivos de transaccin, ni validacin de los datos de xture. Si usas tablas InnoDB tables, no podrs tener referencias hacia adelante en tus archivos de datos MySQL no provee un mecanismo para retrasar el chequeo de las restricciones de registro hasta que la transaccin es realizada.
382
Si ejecutas este script como un usuario con privilegios normales (recomendado), puedes no tener acceso a iniciar un puerto en un nmero de puerto bajo. Los nmeros de puerto bajos son reservados para el superusuario (root). Advertencia: No uses este servidor en una conguracin de produccin. No se le han relizado auditoras de seguridad o tests de performance, y no hay planes de cambiar este hecho. Los desarrolladores de Django estn en el negocio de hacer Web frameworks, no servidores Web, por lo que mejorar este servidor para que pueda manejar un entorno de produccin est fuera del alcance de Django. El servidor de desarrollo carga automticamente el cdigo Python para cada pedido segn sea necesario. No necesitas reiniciar el servidor para que los cambios en el cdigo tengan efecto. Cuando inicias el servidor, y cada vez que cambies cdigo Python mientras el servidor est ejecutando, ste validar todos tus modelos instalados. (Ver la seccin que viene sobre el comando validate.) Si el validador encuentra errores, los imprimir en la salida estndar, pero no detendr el servidor. Puedes ejecutar tantos servidores como quieras, siempre que ejecuten en puertos separados. Slo ejecuta django-admin.py runserver ms de una vez. Observa que la direccin IP por omisin, 127.0.0.1, no es accesible desde las otras mquinas de la red. Para hacer que el servidor de desarrollo sea visible a las otras mquinas de la red, usa su propia direccin IP (ej.: 192.168.2.1) o 0.0.0.0. Por ejemplo, para ejecutar el servidor en el puerto 7000 en la direccin IP 127.0.0.1, usa esto:
django-admin.py runserver 7000
Sirviendo Archivos Estticos con el Servidor de Desarrollo Por omisin, el servidor de desarrollo no sirve archivos estticos para tu sitio (como archivos CSS, imgenes, cosas bajo MEDIA_ROOT_URL, etc.). Si quieres congurar Django para servir medios estticos, lee acerca de esto en http://www.djangoproject.com/documentation/0.96/static_les/. Deshabilitando Autoreload Para deshabilitar la recarga automtica del cdigo mientras el servidor de desarrollo se ejecuta, usa la opcin --noreload, como en:
django-admin.py runserver --noreload
30.2.12 shell
Inicia el intrprete interactivo de Python. Django utilizar IPython (http://ipython.scipy.org/) si no est instalado. Si tienes IPython instalado y quieres forzar el uso del intrprete Python plano, usa la opcin --plain, como en:
django-admin.py shell --plain
383
384
30.2.22 syncdb
Crea las tablas de la base de datos para todas las aplicaciones en INSTALLED_APPS cuyas tablas an no hayan sido creadas. Usa este comando cuando hayas agregado nuevas aplicaciones a tu proyecto y quieras instalarlas en la base de datos. Esto incluye cualquier aplicacin incorporada en Django que est en INSTALLED_APPS por omisin. Cuando empieces un nuevo proyecto, ejecuta este comando para instalas las aplicaciones predeterminadas. Si ests instalando la aplicacin django.contrib.auth, syncdb te dar la opcin de crear un superusuario inmediatamente. syncdb tambin buscar e instalar algun xture llamado initial_data. Ver la documentacin de loaddata para los detalles de la especicacin de los archivos de datos de xture.
30.2.23 test
Descubre y ejecuta las pruebas para todos los modelos instalados. El testeo an est en desarrollo mientras se escribe este libro, as que para aprender ms necesitars leer la documentacin online en http://www.djangoproject.com/documentation/0.96/testing/.
30.2.24 validate
Valida todos los modelos instalados (segn la conguracin de INSTALLED_APPS) e imprime errores de validacin en la salida estndar.
30.3.1 settings
Ejemplo de uso:
django-admin.py syncdb --settings=mysite.settings
Especica explcitamente el mdulo de conguracin a usar. El mdulo de conguracin debe estar en la sintaxis de paquetes de Python (ej.: mysite.settings). Si no se proveen, django-admin.py utilizar la variable de entorno DJANGO_SETTINGS_MODULE. Observa que esta opcin no es necesaria en manage.py, ya que toma en cuenta la conguracin de DJANGO_SETTINGS_MODULE por t.
30.3.2 pythonpath
Ejemplo de uso:
django-admin.py syncdb --pythonpath=/home/djangoprojects/myproject
Agrega la ruta del sistema de archivos a la ruta de bsqueda de importacin de Python. Si no se dene, django-admin.py usar la variable de entorno PYTHONPATH. Observa que esta opcin no es necesaria en manage.py, ya que tiene cuidado de congurar la ruta de Python por t.
385
30.3.3 format
Ejemplo de uso:
django-admin.py dumpdata --format=xml
Especica el formato de salida que ser utilizado. El nombre provisto debe ser el nombre de un serializador registrado.
30.3.4 help
Muestra un mensaje de ayuda que incluye una larga lista de todas las opciones y acciones disponibles.
30.3.5 indent
Ejemplo de uso:
django-admin.py dumpdata --indent=4
Especica el nmero de espacios que se utilizarn para la indentacin cuando se imprima una salida con formato de impresin. Por omisin, la salida no tendr formato de impresin. El formato de impresin solo estar habilitado si se provee la opcin de indentacin.
30.3.6 noinput
Indica que no quieres que se te pida ninguna entrada. Es til cuando el script django-admin se ejecutar en forma automtica y desatendida.
30.3.7 noreload
Deshabilita el uso del autoreloader cuando se ejecuta el servidor de desarrollo.
30.3.8 version
Muestra la versin actual de Django. Ejemplo de salida:
0.9.1 0.9.1 (SVN)
30.3.9 verbosity
Ejemplo de uso:
django-admin.py syncdb --verbosity=2
Determina la cantidad de noticaciones e informacin de depuracin que se imprimir en la consola. 0 es sin salida, 1 es salida normal, y 2 es salida con explicaciones.
386
30.3.10 adminmedia
Ejemplo de uso:
django-admin.py --adminmedia=/tmp/new-admin-style/
Le dice a Django donde encontrar los archivos CSS y JavaScript para la interfaz de administracin cuando se ejecuta el servidor de desarrollo. Normalmente estos archivos son servidos por fuera del arbol de fuentes Django pero como algunos diseadores personalizan estos archivos para su sitio, esta opcin te permite testear con versiones personalizadas.
387
388
CAPTULO 31
Django usa los objetos respuesta y peticin para pasar informacin de estado a travs del sistema. Cuando se peticiona una pgina, Django crea un objeto HttpRequest que contiene metadatos sobre la peticin. Luego Django carga la vista apropiada, pasando el HttpRequest como el primer argumento de la funcin de vista. Cada vista es responsable de retornar un objeto HttpResponse. Hemos usado estos objetos con frecuencia a lo largo del libro; este apndice explica las APIs completas para los objetos HttpRequest y HttpResponse.
31.1 HttpRequest
HttpRequest representa una sola peticin HTTP desde algn agente de usuario. Mucha de la informacin importante sobre la peticin esta disponible como atributos en la instancia de HttpRequest (mira la Tabla H-1). Todos los atributos excepto session deben considerarse de slo lectura.
389
Atributo path
method
Descripcin Un string que representa la ruta completa a la pgina peticionada, no incluye el dominio por ejemplo, /music/bands/the_beatles/". Un string que representa el mtodo HTTP usado en la peticin. Se garantiza que estar en maysculas. Por ejemplo: if request.method == GET : do_something() elif request.method == POST : do_something_else() Un objeto similar a un diccionario que contiene todos los parmetros HTTP GET dados. Mira la documentacin de QueryDict que sigue. Un objeto similar a un diccionario que contiene todos los parmetros HTTP POST dados.Mira la documentacin de QueryDict que sigue. Es posible que una peticin pueda ingresar va POST con un diccionario POST vaco si, digamos, un formulario es peticionado a travs del mtodo HTTP POST pero que no incluye datos de formulario. Por eso, no deberas usar if request.POST para vericar el uso del mtodo POST; en su lugar, utiliza if request.method == "POST" (mira la entrada method en esta tabla). Nota: POST no incluye informacin sobre la subida de archivos. Mira FILES. Por conveniencia, un objeto similar a un diccionario que busca en POST primero, y luego en GET. Inspirado por $_REQUEST de PHP. Por ejemplo, si GET = {"name": "john"} y POST = {.age": 34}, REQUEST["name"] ser "john", y REQUEST[.age"] ser "34". Se sugiere encarecidamente que uses GET y POST en lugar de REQUEST, ya que lo primero es ms explcito. Un diccionario Python estndar que contiene todas las cookies. Las claves y los valores son strings. Mira el Captulo 12 para saber mas de cookies. Un objeto similar a un diccionario que contiene todos los archivos subidos. Cada clave de FILES es el atributo name de <input type="file"name= />. Cada valor de FILES es un diccionario Python estndar con las siguientes tres claves: filename: El nombre del archivo subido, como un string Python. content-type: El tipo de contenido del archivo subido. content: El contenido en crudo del archivo subido. Nota que FILES contendr datos slo si el mtodo de la peticin fue POST y el <form> que realiz la peticin contena Captulo 31. Apndice H: Objetos Peticin y Respuesta enctype="multipart/form-data". De lo contrario, FILES ser un objeto similar a un diccionario vaco. Un diccionario Python estndar que contiene to-
GET
POST
REQUEST
COOKIES
FILES
390
META
Los objetos request tambin tienen algunos mtodos de utilidad, como se muestra en la Tabla H-2. Mtodo Descripcin __getitem__(key) el valor GET/POST para la clave dada, vericando POST primero, y luego Retorna GET. Emite KeyError si la clave no existe. Esto te permite usar sintaxis de acceso a diccionarios en una instancia HttpRequest. Por ejemplo, request["foo"] es lo mismo que comprobar request.POST["foo"] y luego request.GET["foo"]. has_key() Retorna True o False, sealando si request.GET o request.POST contiene la clave dada. get_full_path() la ruta, ms un string de consulta agregado. Por ejemplo, Retorna /music/bands/the_beatles/?print=true" is_secure()Retorna True si la peticin es segura; es decir si fue realizada con HTTPS.
31.1. HttpRequest
391
Mtodo __getitem__
__setitem__
get() update()
Diferencias con la implementacin estndar de dict Funciona como en un diccionario. Sin embargo, si la clave tiene ms de un valor, __getitem__() retorna el ltimo valor. Establece la clave dada a [value] (una lista de Python cuyo nico elemento es value). Nota que sta, como otras funciones de diccionario que tienen efectos secundarios, slo puede ser llamada en un QueryDict mutable (uno que fue creado va copy()). Si la clave tiene ms de un valor, get() retorna el ltimo valor al igual que __getitem__. Recibe ya sea un QueryDict o un diccionario estndar. A diferencia del mtodo update de los diccionarios estndar, este mtodo agrega elementos al diccionario actual en vez de reemplazarlos: >>> q = QueryDict( a=1 ) >>> q = q.copy() # to make it mutable >>> q.update({ a : 2 }) >>> q.getlist( a ) [1, 2] >>> q[ a ] # returns the last [2] Similar al mtodo items() de un diccionario estndar, excepto que ste utiliza la misma lgica del ltimo-valor de __getitem()__: >>> q = QueryDict( a=1&a=2&a=3 ) >>> q.items() [(a, 3)] Similar al mtodo values() de un diccionario estndar, excepto que este utiliza la misma lgica del ltimo-valor de __getitem()__.
items()
values()
392
Mtodo copy()
getlist(key)
Descripcin Retorna una copia del objeto, utilizando copy.deepcopy() de la biblioteca estndar de Python. La copia ser mutable es decir, puedes cambiar sus valores. Retorna los datos de la clave requerida, como una lista de Python. Retorna una lista vaca si la clave no existe. Se garantiza que retornar una lista de algn tipo. Establece la clave dada a list_ (a diferencia de __setitem__()). Agrega un elemento item a la lista interna asociada a key. Igual a setdefault, excepto que toma una lista de valores en vez de un slo valor. Similar a items(), excepto que incluye todos los valores, como una lista, para cada miembro del diccionario. Por ejemplo: >>> q = QueryDict( a=1&a=2&a=3 ) >>> q.lists() [(a, [1, 2, 3])] Retorna un string de los datos en formato querystring (ej., .a=2&b=3&b=5").
urlencode()
Si el usuario ingresa "John Smith" en el campo your_name y selecciona tanto The Beatles como The Zombies en la caja de seleccin mltiple, lo siguiente es lo que contendr el objeto request de Django:
>>> request.GET {} >>> request.POST {your_name: [John Smith], bands: [beatles, zombies]} >>> request.POST[ your_name ] John Smith >>> request.POST[ bands ] zombies >>> request.POST.getlist( bands ) [beatles, zombies] >>> request.POST.get( your_name , Adrian ) John Smith >>> request.POST.get( nonexistent_field , Nowhere Man ) Nowhere Man
Nota de implementacin:
31.1. HttpRequest
393
Los atributos GET, POST, COOKIES, FILES, META, REQUEST, raw_post_data, y user son todos cargados tardamente. Esto signica que Django no gasta recursos calculando los valores de estos atributos hasta que tu cdigo los solicita.
31.2 HttpResponse
A diferencia de los objetos HttpRequest, los cuales son creados automticamente por Django, los objetos HttpResponse son tu responsabilidad. Cada vista que escribas es responsable de instanciar, poblar, y retornar un HttpResponse. La clase HttpResponse esta ubicada en django.http.HttpResponse.
Pero si quieres agregar contenido de manera incremental, puedes usar response como un objeto similar a un archivo:
>>> response = HttpResponse() >>> response.write( " <p>Here s the text of the Web page.</p> " ) >>> response.write( " <p>Here s another paragraph.</p> " )
Puedes pasarle a HttpResponse un iterador en vez de pasarle strings codicadas a mano. Si utilizas esta tcnica, sigue estas instrucciones: El iterador debe retornar strings. Si un HttpResponse ha sido inicializado con un iterador como su contenido, no puedes usar la instancia HttpResponse como un objeto similar a un archivo. Si lo haces, emitir Exception. Finalmente, nota que HttpResponse implementa un mtodo write(), lo cual lo hace apto para usarlo en cualquier lugar que Python espere un objeto similar a un archivo. Mira el Captulo 11 para ver algunos ejemplos de la utilizacin de esta tcnica.
Puedes utilizar tambin has_header(header) para vericar la existencia de una cabecera. Evita congurar cabeceras Cookie a mano; en cambio, mira el Captulo 12 para instrucciones sobre como trabajan las cookies en Django.
394
HttpResponsePermanentRedirect
HttpResponseNotModified
HttpResponseNotAllowed
HttpResponseGone
HttpResponseServerError
Puedes, por supuesto, denir tus propias subclases de HttpResponse para permitir diferentes tipos de respuestas no admitidas por las clases estndar.
Debido a que el error 404 es por mucho el error HTTP ms comn, hay una manera ms fcil de manejarlo.
31.2. HttpResponse
395
Cuando retornas un error tal como HttpResponseNotFound, eres responsable de denir el HTML de la pgina de error resultante:
return HttpResponseNotFound( <h1>Page not found</h1> )
Por consistencia, y porque es una buena idea tener una pgina de error 404 consistente en todo tu sitio, Django provee una excepcin Http404. Si tu emites una Http404 en cualquier punto de una vista de funcin, Django la atrapar y retornar la pgina de error estndar de tu aplicacin, junto con un cdigo de error HTTP 404. ste es un ejemplo:
from django.http import Http404 def detail(request, poll_id): try: p = Poll.objects.get(pk=poll_id) except Poll.DoesNotExist: raise Http404 return render_to_response( polls/detail.html , { poll : p})
Con el n de usar la excepcin Http404 al mximo, deberas crear una plantilla que se muestra cuando un error 404 es emitido. Esta plantilla debera ser llamada 404.html, y debera colocarse en el nivel superior de tu rbol de plantillas.
Detrs de escena, Django determina la vista 404 buscando por handler404. Por omisin, las URLconfs contienen la siguiente lnea:
from django.conf.urls.defaults import *
Esto se encarga de establecer handler404 en el mdulo actual. Como puedes ver en django/conf/urls/defaults.py, handler404 esta jado a django.views.defaults.page_not_found por omisin. Hay tres cosas para tener en cuenta sobre las vistas 404: La vista 404 es llamada tambin si Django no encuentra una coincidencia despus de vericar toda expresin regular en la URLconf. Si no denes tu propia vista 404 y simplemente usas la predeterminada, lo cual es recomendado tu an tienes una obligacin: crear una plantilla 404.html en la raz de tu directorio de plantillas. La vista 404 predeterminada usar esa plantilla para todos los errores 404.
396
Si DEBUG esta establecido a True (en tu modulo de conguracin), entonces tu vista 404 nunca ser usada, y se mostrar en su lugar el trazado de pila.
31.2. HttpResponse
397
398
CAPTULO 32
Sobre el libro
Ests leyendo El libro de Django, publicado en Diciembre de 2007 por Apress con el ttulo The Denitive Guide to Django: Web Development Done Right. Hemos lanzado este libro libremente por un par de razones. La primera es que amamos Django y queremos que sea tan accesible como sea posible. Muchos programadores aprenden su arte desde material tcnico bien escrito, as que nosotros intentamos escribir una gua destacada que sirva adems como referencia para Django. La segunda, es que resulta que escribir libros sobre tecnologa es particularmente difcil: sus palabras se vuelven anticuadas incluso antes de que el libro llegue a la imprenta. En la web, sin embargo, la tinta nunca se seca podremos mantener este libro al da (y as lo haremos) . La respuesta de los lectores es una parte crtica de ese proceso. Hemos construido un sistema de comentarios que te dejar comentar sobre cualquier parte del libro; leeremos y utilizaremos estos comentarios en nuevas versiones.
399