Изменение версии по умолчанию в движке приложения
С тех пор, как был выпущен движок приложения 1.4.2, я получаю предупреждения, подобные этому, в моих производственных журналах:
Вы используете версию Django по умолчанию (0.96). Версия Django по умолчанию изменится в версии App Engine в ближайшем будущем. Пожалуйста, вызовите use_library(), чтобы явно выбрать версию Django. Для получения дополнительной информации см. http://code.google.com/appengine/docs/python/tools/libraries.html
Это происходит на каждом обработчике, где я использую шаблон Django - через следующее:
from google.appengine.ext.webapp import template
Я хотел бы обновить до 1.2, однако следующие ссылки не совсем ясно, как именно это сделать (или работает ли он вообще):
- http://code.google.com/appengine/docs/python/tools/libraries.html
- 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
Общий поток должен вставить это:
from google.appengine.dist import use_library
use_library('django', '1.2')
Тем не менее, в какой файл (ы) это должно быть вставлено:
- Просто в appengine_config.py?
- В каждом файле.py, который делает
from google.appengine.ext.webapp import template
? - В каждом файле.py в проекте?
- В 1 и (2 или 3) выше, а также добавить
import appengine_config
к этим файлам? - В 3 или 4, а также добавить оболочки вокруг встроенных функций, таких как appstats, удаленный API, администратора хранилища данных и т. Д.?
- Что-то другое?
Благодарю.
4 ответа
Как описано Ником в комментариях к ответу systempuntoout, я вставил этот use_library()
код отсюда в каждом обработчике, который импортирует django (напрямую или через google.appengine.ext.webapp.template
или даже просто django.utils.simplejson
):
from google.appengine.dist import use_library
use_library('django', '1.2')
Как предположил Ник, это стало проще благодаря первому рефакторингу, чтобы минимизировать количество обработчиков, на которые ссылается app.yaml (то есть ближе к сценарию 1, описанному здесь).
Тем не менее, у меня настроен встроенный appstats, и если я сначала захожу в /_ah/appstats после загрузки, то я получаю эту ошибку:
<'google.appengine.dist._library.UnacceptableVersionError'>: запрошен django 1.2, но 0.96.4. Ни один уже не используется
Я смог исправить это, в том числе и use_library()
код в appengine_config.py
,
Я заметил, что, вставив вызов use_library()
в appengine_config.py
тогда это больше не было необходимо во всех моих обработчиках. В частности те, которые импортируют google.appengine.ext.webapp.template
не нужно, потому что импорт webapp.template
грузы appengine_config.py
, Импорт интерфейса приложения webapp.template
Вот почему это решило эту проблему.
Тем не менее, у меня были некоторые обработчики (например, службы JSON), которые не импортируют webapp.template
, но сделать импорт django.utils.simplejson
, Эти обработчики по-прежнему требуют прямого вызова use_library()
, В противном случае, если эти обработчики вызываются первыми в новом экземпляре, UnacceptableVersionError
происходит. Хотя я использую appengine_config.py
настроить appstats, то есть appengine_config.py
вызывается для обработки всех запросов, слишком поздно вызывается в жизненном цикле страницы для правильной настройки правильной версии Django.
Поначалу все это работало нормально, но потом я обнаружил обратную несовместимость между новым Django 1.2 и старым Django 0.96, который я использовал. Моя структура проекта выглядит так:
root
+- admin
| +- page_admin.html
+- page_base.html
С Django 0.96, следующее в page_admin.html работало нормально:
{% extends "../page_base.html" %}
С Django 1.2 я получил эту ошибку:
TemplateDoesNotExist:../page_base.html
Изменения в Django 1.2, по-видимому, заключаются в том, что по умолчанию Django не позволяет загружать шаблоны, которые находятся над каталогом исходного шаблона.
Обходной путь для этого описан здесь, но этот подход не может работать для меня, так как он требует, чтобы шаблоны находились в подкаталоге шаблонов.
Решением этой проблемы является создание settings.py
файл, установите TEMPLATE_DIRS
установить в корневой каталог проекта, а затем изменить extends
тег просто ссылка "page_base.html"
, как описано здесь. Однако я столкнулся с двумя проблемами, пытаясь это сделать.
Я использовал рекомендованный код для рендеринга моего шаблона, а именно:
template_values = { ... }
path = os.path.join(os.path.dirname(__file__), 'page_admin.html')
self.response.out.write(template.render(path, template_values))
Первая проблема заключается в том, что template.render()
переопределяет TEMPLATE_DIRS
установка, чтобы установить его в каталог отображаемого шаблона. Решением этого является следующий код:
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)))
Один недостаток этого подхода заключается в том, что template.render()
кэширует скомпилированные шаблоны, тогда как этот код этого не делает (хотя это не составит труда добавить).
Чтобы настроить TEMPLATE_DIRS
настройки, я добавил settings.py
к моему проекту:
PROJECT_ROOT = os.path.dirname(__file__)
TEMPLATE_DIRS = (PROJECT_ROOT,)
А потом во всех моих обработчиках, до use_library()
код, я установил DJANGO_SETTINGS_MODULE
как описано здесь:
import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
Вторая проблема заключалась в том, что это не сработало - файл настроек не загружался, и поэтому TEMPLATE_DIRS
был пуст.
Настройки Django загружаются из указанного settings.py
лениво, первый раз к ним обращаются. Проблема в том, что импорт webapp.template
звонки django.conf.settings.configure()
попытаться установить некоторые настройки. Поэтому если webapp.template
импортируется до доступа к любым настройкам, затем settings.py
никогда не загружается (поскольку средство доступа к настройкам обнаруживает, что настройки уже существуют и больше не пытается их загружать).
Решением этой проблемы является принудительный доступ к настройкам, загрузка settings.py
, до webapp.template
импортируется. Тогда, когда webapp.template
позже импортируется, его вызов django.conf.settings.configure()
игнорируется Поэтому я изменил код установки версии Django во всех моих обработчиках (и appengine_config.py
) к следующему:
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
На практике я фактически помещаю весь приведенный выше код в файл с именем setup_django_version.py
, а затем импортировать это из всех моих обработчиков, вместо того, чтобы дублировать эти 6 строк кода везде.
Затем я обновил свой page_admin.html
шаблон, чтобы включить это (т.е. указать page_base.html
относительно TEMPLATE_DIRS
установка):
{% extends "page_base.html" %}
И это решило проблему с отображением страницы администратора.
Начиная с GAE 1.5.0, существует гораздо более простой, хотя и недокументированный, способ указать, какую версию шаблонов Django вы хотите использовать.
В appengine_config.py
, включите строку
webapp_django_version = '1.2'
Вот и все.
Больше не нужно use_library()
,
Согласно документации, которую вы правильно связываете, вы должны просто добавить эту функцию в начале main.py
обработчик сценариев.
Одна вещь, которую я хотел бы отметить, что документация не проясняет: если вы используете google.appengine.ext.deferred
и имеют use_library
в вашем main.py
тогда, когда отложенное задание будет выполнено, оно НЕ будет загружено main.py
и если вам не повезло иметь отложенную задачу в качестве первого запроса к экземпляру, он будет блокировать экземпляр (вызывая его UnacceptableVersionError
Когда ваш main.py
попытки позвонить use_library
на более поздний запрос). Я думаю, если вы добавите use_libary
в appengine_config.py
это будет работать с deferred
также, но мы закончили тем, что переключились на обычные очереди задач (через которые обработчики проходят через main.py
) чтобы избежать этой проблемы.