Изменение версии Django по умолчанию для движка приложений

С тех пор, как был выпущен движок приложения 1.4.2, я получаю подобные предупреждения в моих рабочих журналах:

Вы используете версию Django по умолчанию (0.96). Версия Django по умолчанию будет изменена в выпуске App Engine в ближайшем будущем. Пожалуйста, вызовите use_library (), чтобы явно выбрать версию Django. Для получения дополнительной информации см. http://code.google.com/appengine/docs/python/tools/libraries.html#Django

Это происходит в каждом обработчике, в котором я использую шаблон Django, с помощью следующего:

from google.appengine.ext.webapp import template

Я хотел бы перейти на 1.2, однако следующие ссылки кажутся не очень ясными относительно того, как именно это сделать (и работает ли это вообще):

Обычная нить состоит в том, чтобы вставить это:

from google.appengine.dist import use_library
use_library('django', '1.2')

Однако в какой файл (а) это нужно вставить:

  1. Просто в appengine_config.py?
  2. В каждом файле .py, который делает from google.appengine.ext.webapp import template?
  3. В каждом файле .py в проекте?
  4. В пунктах 1 и (2 или 3) выше, а также добавить import appengine_config к этим файлам?
  5. В 3 или 4, а также добавить оболочки для встроенных функций, таких как appstats, удаленный API, администратор хранилища данных и т. Д.?
  6. Что-то другое?

Спасибо.


person Saxon Druce    schedule 14.02.2011    source источник


Ответы (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. Пользовательский интерфейс appstats импортирует webapp.template, поэтому эта проблема решена.

Однако у меня были обработчики (например, json-сервисы), которые не импортируют webapp.template, но импортируют django.utils.simplejson. Эти обработчики по-прежнему требуют прямого вызова use_library(). В противном случае, если эти обработчики вызываются первыми в новом экземпляре, возникает UnacceptableVersionError. Хотя я использую appengine_config.py для настройки статистики приложений, то есть 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" %}

И это устранило проблему с отображением админки.

person Saxon Druce    schedule 17.02.2011
comment
+1 Спасибо за вашу работу по выяснению этого. У меня была такая же проблема, когда я изменил свою версию Django на 1.2, и вы сэкономили мне много времени. - person Bryce Cutt; 28.02.2011
comment
Отличный ответ. Отвечает на вопрос, а также на все возможные дополнительные вопросы. - person Mendelt; 07.04.2011
comment
Что меня сбило с толку, так это то, что после добавления моей переменной TEMPLATE_DIRS она все еще не работала. Оказывается, это произошло потому, что я все еще использовал загрузчик шаблонов пользовательского движка приложений. Как только я переключился на загрузчик шаблонов django, он начал работать. - person Richard Nienaber; 28.06.2011
comment
Было бы действительно полезно, если бы документация по App Engine была связана с этим. - person Fraser Harris; 12.04.2012
comment
это безумие, что это должно быть так сложно. - person HorseloverFat; 18.01.2013
comment
Спасибо. Нам нужно гораздо больше документации по GAE :-( вы нам нужны :-) - person Aerox; 15.06.2014

Начиная с GAE 1.5.0, существует гораздо более простой, хотя на данный момент недостаточно документированный способ указать, какую версию шаблонов Django вы хотите использовать.

В appengine_config.py включите строку

webapp_django_version = '1.2'

Вот и все.

Больше нет необходимости в use_library().

person Dave W. Smith    schedule 09.06.2011
comment
Параметр webapp_django_version фактически существовал до 1.5.0, но все еще имеет некоторые проблемы. Из каждого обработчика, который импортирует django (каталог или косвенно), вам нужно сначала убедиться, что у вас есть «шаблон импорта из google.appengine.ext.webapp», иначе он не удастся. Также в более старых версиях до 1.5.0 при использовании этого метода по-прежнему будет проблема с игнорированием файла settings.py, о котором я упоминал. Похоже, это было исправлено в 1.5.0. Теперь это выглядит как эффективный прием, если вы импортируете библиотеку шаблонов повсюду, что несложно :) - person Saxon Druce; 09.06.2011
comment
Обратите внимание, что это не работает при использовании среды выполнения Python 2.7. См. stackoverflow.com/a/6536723/201828. - person phatmann; 21.07.2012

Согласно документации, которую вы правильно связываете, вы должны просто добавьте эту функцию в начало обработчика main.py скрипта.

person systempuntoout    schedule 14.02.2011
comment
@systempuntoout: у меня нет обработчика сценария main.py - мой app.yaml имеет около 20 обработчиков, указывающих на разные файлы .py, в каждом из которых есть около 1-10 классов обработчиков. Некоторые из них используют шаблоны, а некоторые нет (например, службы и задачи). - person Saxon Druce; 15.02.2011
comment
@Nick: Я думал, что нет конкретной рекомендации о том, следует ли вам указывать app.yaml на файлы N .py с обработчиками в них или app.yaml указывать на 1 файл .py с N обработчиками в нем? - person Saxon Druce; 15.02.2011
comment
@Nick: например, см. stackoverflow.com/questions/3025921/ - person Saxon Druce; 15.02.2011
comment
@Saxon Нет, но на самом деле вы ничего не добьетесь, если разделите это до такой степени и создадите себе при этом большую боль. - person Nick Johnson; 15.02.2011
comment
@Nick: Так мне нужно вызывать use_library() из каждого обработчика, который вызывает app.yaml? В таком случае, да, это немного болезненно :) В остальном это было довольно безболезненно, и хорошо справляется с разделением функциональных областей сайта :) Теоретически я мог бы сократить его до 2 (например, один для большую часть сайта и один для login: admin URL-адресов), но это будет немного похоже на то, что я комбинирую несвязанные функции. Может быть, около 4-х было бы более приятным компромиссом, например, администратор, службы, задачи, страницы (в дополнение к встроенным функциям, таким как appstats, удаленный api и т. Д.). - person Saxon Druce; 15.02.2011
comment
@Saxon Вам нужно вызвать его - или импортировать вызывающий его модуль - из каждого обработчика, использующего Django, напрямую или транзитивно. Лично я бы рекомендовал использовать один обработчик для каждого более или менее независимого компонента вашего приложения - main и admin являются естественными делениями. - person Nick Johnson; 15.02.2011
comment
Спасибо, Ник. Я немного реорганизовал свой проект, а затем поместил use_library() во все свои обработчики. Однако я обнаружил, что если я сначала перейду в / _ah / appstats после загрузки, а затем на свой сайт, это не удастся с <'google.appengine.dist._library.UnacceptableVersionError'>: django 1.2 was requested, but 0.96.4.None is already in use. Кажется, я исправил это, добавив use_library() в appengine_config.py. - person Saxon Druce; 15.02.2011
comment
Спасибо Нику за обсуждение и спасибо systempuntoout за ваш ответ, который вызвал обсуждение :) Я столкнулся с еще некоторыми проблемами при работе с Django 1.2, поэтому я написал отдельный ответ о том, что мне нужно было сделать, чтобы заставить его работать. - person Saxon Druce; 17.02.2011
comment
Я вижу эту ошибку в файлах журнала моего сервера разработки, хотя я не использую и не импортирую django в каких-либо обработчиках. Есть ли способ узнать, какой обработчик вызывает это предупреждение? - person Zeynel; 08.10.2011

Одна вещь, которую я хотел бы упомянуть, не содержит документации t проясните: если вы используете google.appengine.ext.deferred и имеете use_library в своем main.py, то при отложенном задача выполнена, она НЕ будет загружаться main.py, и если вам не повезло, и отложенная задача будет вашим первым запросом к экземпляру, она остановит этот экземпляр (заставляя его бросать UnacceptableVersionError, когда ваш main.py пытается выполнить позвоните use_library по более позднему запросу). Я думаю, если вы добавите use_libary в appengine_config.py, он также будет работать с deferred , но в конечном итоге мы переключились на обычные очереди задач (обработчики которых ПРОУЧИВАЮТСЯ через main.py), чтобы избежать этой проблемы.

person Tyler Brandt    schedule 05.06.2013