google app engine - standard - Cambio de versión de Django predeterminado del motor de la aplicación
google sdk app engine (4)
Desde que se lanzó el motor de aplicación 1.4.2, recibo advertencias como esta en mis registros de producción:
Está utilizando la versión predeterminada de Django (0.96). La versión predeterminada de Django cambiará en una versión de App Engine en el futuro cercano. Llame a use_library () para seleccionar explícitamente una versión de Django. Para obtener más información, consulte http://code.google.com/appengine/docs/python/tools/libraries.html#Django
Esto ocurre en cada controlador donde uso una plantilla de Django, a través de lo siguiente:
from google.appengine.ext.webapp import template
Me gustaría actualizar a 1.2, sin embargo, los siguientes enlaces no parecen muy claros sobre exactamente cómo hacer esto (o si funciona en absoluto):
- http://code.google.com/appengine/docs/python/tools/libraries.html#Django
- http://code.google.com/p/googleappengine/issues/detail?id=1758
- http://code.google.com/p/googleappengine/issues/detail?id=4489
- http://www.mediacrafters.org/post/django11-on-appengine
El hilo común es insertar esto:
from google.appengine.dist import use_library
use_library(''django'', ''1.2'')
Sin embargo, en qué archivo (s) debe insertarse esto:
- ¿Solo en appengine_config.py?
- En cada archivo .py que hace
from google.appengine.ext.webapp import template
? - En cada archivo .py en el proyecto?
- En 1 y (2 o 3) arriba, y también agregue
import appengine_config
a esos archivos? - ¿En 3 o 4, y también agregue envoltorios alrededor de funciones incorporadas como appstats, api remoto, administrador del almacén de datos, etc.?
- ¿Algo más?
Gracias.
A partir de GAE 1.5.0, hay una manera mucho más simple, aunque momentáneamente documentada, de especificar qué versión de las plantillas de Django quieres usar.
En appengine_config.py
, incluye la línea
webapp_django_version = ''1.2''
Eso es.
No más necesidad de use_library()
.
Como lo describe Nick en los comentarios de la respuesta de systempuntoout, use_library()
este código use_library()
http://code.google.com/appengine/docs/python/tools/libraries.html#Django en cada controlador que importa django (ya sea directamente o a través de google.appengine.ext.webapp.template
o incluso solo django.utils.simplejson
):
from google.appengine.dist import use_library
use_library(''django'', ''1.2'')
Como sugirió Nick, esto se hizo más fácil al refactorizar primero para minimizar el número de manejadores a los que se hace referencia en app.yaml (es decir, más cerca del escenario 1 descrito aquí ).
Sin embargo, tengo configurado el administrador de aplicaciones, y si primero fui a / _ah / appstats después de una carga, entonces obtendría este error:
<''google.appengine.dist._library.UnacceptableVersionError''>: se solicitó django 1.2, pero 0.96.4.None ya está en uso
Pude arreglar esto incluyendo también el código use_library()
en appengine_config.py
.
Noté que al insertar una llamada a use_library()
en appengine_config.py
, ya no era necesario en todos mis controladores. En particular, los que importan google.appengine.ext.webapp.template
no lo necesitan, porque la importación de webapp.template
carga appengine_config.py
. La interfaz de usuario appstats importa webapp.template
, que es la razón por la cual este problema se solucionó.
Sin embargo, tenía algunos controladores (por ejemplo, servicios json) que no importan webapp.template
, pero importan django.utils.simplejson
. Estos controladores aún requieren una llamada directa a use_library()
. De lo contrario, si esos manejadores son llamados primero en una nueva instancia, ocurre el UnacceptableVersionError
. Aunque estoy usando appengine_config.py
para configurar appstats, lo que significa que se llama a appengine_config.py
para instrumentar todas las solicitudes, se llama demasiado tarde en el ciclo de vida de la página para configurar correctamente la versión correcta de Django.
Todo parecía funcionar bien al principio, pero luego descubrí una incompatibilidad hacia atrás entre el nuevo Django 1.2 y el viejo Django 0.96 que había estado usando. Mi estructura de proyecto es así:
root
+- admin
| +- page_admin.html
+- page_base.html
Con Django 0.96, tener lo siguiente en page_admin.html funcionó bien:
{% extends "../page_base.html" %}
Con Django 1.2, obtuve este error:
TemplateDoesNotExist: ../page_base.html
El cambio en Django 1.2 parece ser que, por defecto, Django no permite cargar plantillas que están por encima del directorio de la plantilla original.
here se describe una solución para esto, pero este enfoque no podría funcionar para mí, ya que requiere que las plantillas estén en un subdirectorio de plantillas.
La solución a esto es configurar un archivo settings.py
, establecer la configuración TEMPLATE_DIRS
en el directorio raíz del proyecto y luego cambiar la etiqueta extends
para hacer referencia a "page_base.html"
, como se describe aquí . Sin embargo, me encontré con dos problemas tratando de hacer esto.
Estaba usando el código recomendado para renderizar mi plantilla, es decir:
template_values = { ... }
path = os.path.join(os.path.dirname(__file__), ''page_admin.html'')
self.response.out.write(template.render(path, template_values))
El primer problema es que template.render()
anula la configuración de TEMPLATE_DIRS
, para establecerlo en el directorio de la plantilla que se está procesando. La solución a esto es el siguiente código:
template_values = { ... }
path = os.path.join(os.path.dirname(__file__), ''page_admin.html'')
template_file = open(path)
compiled_template = template.Template(template_file.read())
template_file.close()
self.response.out.write(compiled_template.render(template.Context(template_values)))
Una desventaja de este enfoque es que template.render()
almacena en caché las plantillas compiladas, mientras que este código no (aunque eso no sería difícil de agregar).
Para configurar la configuración de TEMPLATE_DIRS
, agregué un settings.py
a mi proyecto:
PROJECT_ROOT = os.path.dirname(__file__)
TEMPLATE_DIRS = (PROJECT_ROOT,)
Y luego, en todos mis controladores, antes del código use_library()
, configuré DJANGO_SETTINGS_MODULE
http://code.google.com/appengine/docs/python/tools/libraries.html#Django :
import os
os.environ[''DJANGO_SETTINGS_MODULE''] = ''settings''
El segundo problema fue que esto no funcionó: el archivo de configuración no se estaba cargando, por lo que TEMPLATE_DIRS
estaba vacío.
La configuración de Django se carga desde la settings.py
especificada settings.py
perezosamente, la primera vez que se accede a ellos. El problema es que la importación de webapp.template
llama a django.conf.settings.configure()
para intentar establecer algunas configuraciones. Por lo tanto, si webapp.template
se importa antes de acceder a cualquier configuración, entonces settings.py
nunca se carga (ya que el acce- webapp.template
configuraciones encuentra que la configuración ya existe y no intenta cargar más).
La solución a esto es forzar el acceso a las configuraciones, cargar las settings.py
, antes de que webapp.template
sea importado. Luego, cuando webapp.template
se importa más tarde, se ignora su llamada a django.conf.settings.configure()
. Por lo tanto, cambié el código de configuración de la versión de Django en todos mis controladores (y appengine_config.py
) a lo siguiente:
import os
os.environ[''DJANGO_SETTINGS_MODULE''] = ''settings''
from google.appengine.dist import use_library
use_library(''django'', ''1.2'')
from django.conf import settings
_ = settings.TEMPLATE_DIRS
En la práctica, puse todo el código anterior en un archivo llamado setup_django_version.py
, y luego lo setup_django_version.py
de todos mis manejadores, en lugar de duplicar estas 6 líneas de código en todas partes.
Luego actualicé mi plantilla page_admin.html
para incluir esto (es decir, especifico page_base.html
relativo a la configuración TEMPLATE_DIRS
):
{% extends "page_base.html" %}
Y eso solucionó el problema de renderizar la página de administración.
De acuerdo con la http://code.google.com/appengine/docs/python/tools/libraries.html#Django que está enlazando correctamente, debe agregar esta función al comienzo de su controlador de script main.py
Una cosa que me gustaría mencionar es que la documentation no deja en claro: si usas google.appengine.ext.deferred
y tienes use_library
en tu main.py
, cuando se ejecuta la tarea diferida NO cargará main.py
y si tiene la mala suerte de tener una tarea diferida como su primera solicitud a una instancia, pondrá la instancia en bork (causando que main.py
el main.py
cuando su main.py
intente llamar a use_library
en una solicitud posterior). Creo que si agrega use_libary
a appengine_config.py
, también funcionará con deferred
, pero terminamos cambiando a colas de tareas normales (qué manejadores están enrutados a través de main.py
) para evitar este problema.