В одну корзину

Один из самых часто задаваемых вопросов на форумах по Джанге -- как на одной странице выводить информацию из разных информационных блоков (пример такой ветки или вот совсем свежий). Давайте попробуем разобраться в стандартных способах решения данной задачи. Я специально не хочу рассматривать тут какие-то сторонние решения просто из-за того, что их много и они не интересны с точки зрения изучения классических методик применения возможностей Джанги.

Основной проблемой с которой сталкиваются вопрошающие -- как получить все нужны данные в одной вьюхе и зачастую из разных приложений. Это усугубляется тем, что у большинства в подсознании сидит необходимость разделения приложений на максимально независимые компоненты. И это правильное желание. Другое дело, что не надо этим принципом злоупотреблять. Если всё-таки ваши приложения нуждаются друг в друге достаточно сильно, то нет ничего плохого в том чтобы иметь в них перекрестные импорты и заимствовать функционал (в том числе по получению данных) так или иначе. Другое дело что таком случае стоит подумать о том, что возможно они настолько жестко связаны, что должны быть единым целым, т.е. одним приложением по сути. Но и даже в этом случае имеет смысл как-то чуть-чуть отделить общие данные от локальных.

Обычно исходной целью стремления получить данные из разных приложений являются всякие виджеты, информеры и прочие "портальные" элементы, которые применятся в современных сайтах и зачастую повторяются в разных разделах. Причем обычно они либо везде выглядят одинаково, либо отличаются от страници к странице, но представляют одни и теже данные.

Итак, Джанга предоставляет вам два разных по сути способа "собрать" на одной странице данные из нескольких компонентов.

Первый это контекст процессоры. Вообще контекст процессоры можно применять для многих прикладных задачи, т.к. по сути они просто добавлять в контекст некоторые глобальные данные. Удобны он ещё и тем, что подключаются в настройках проекта и могут поставляться вместе с отдельными приложениями.

def app_globals(request):
   return {'app_data': …}

Но тут надо помнить, что контекст процессоры глобальны и добавляют данные в контекст всегда, конечно если вы во вьюхах используете RequestContext, и вы не пытаетесь совсем хитрым образом обрабатывать разные request.

Шаблонные теги намного более гибкие и позволяют не просто протолкнуть куда-то данные из приложения, а предоставить ещё возможность для их визуального представления.

Я не люблю теги тупо проталкивающие данные в контекст, например такие:

def do_app_data(parser, token):
   return AppDataNode()

class AppDataNode(template.Node):
   def render(self, context):
      context['app_data'] = ...
      return ''

Их поведение не всегда очевидно и они как бы не придерживаются идеи того, что теги что-то из себя возвращают в документ и не имеют побочных эффектов. При таком их использовании они просто изменят контекст и рендерят пустую строку. Иногда это удобно, но я скептически отношусь к таким ситуациям и предпочитаю их избегать.

Кстати, то что в хелпере simple_tag нельзя получить контекст мне кажется очень разумным -- так не провоцируется его изменение.

Гораздо эффективней использовать inclusion теги. Они позволяют и данные подготовить, и сразу выплюнуть их в шаблон в котором они будут отрисованы и попадут в вывод. При этом достигается максимальная изолированность логики, что очень хорошо сказываться на архитектуре системы.

@register.inclusion_tag('app_widget.html')
def app_widget():
   #...

В дополнение, при использование флага takes_context=True можно получить весь контекст, при необходимости взять оттуда какие-то дополнительные данные или даже прокинуть его целиком в шаблон виджета.

Преимуществ применения шаблонных тегов куча. Их можно подгружать только в тех шаблонах, где предполагается вывод иммено этих данных. Теги позволяют четко разделить функционал внутри системы и не делать засоряющих всё контекст процессоров или функций которые как-то дополняют контекст внутри вьюхи. Их можно добавлять, удалять и редактировать отдельно от вьюх. Так же иногда удобно их применять в ситуациях, когда требуется отрисовать шаблон не зависимо от веб-запроса -- например при отправки каких-то уведомлений на почту, либо ещё чего-то так же оторванного от request/response конвейера. Также теги легко распространяются совместно с отдельными приложениями.

Надо признать, что сейчас в Джанге вся шаблонна подсистема достаточно в грязном состоянии и уже зреет её существенный рефакторинг, который принесет более удобные пути создания теов различной сложности и приведет к общему знаменателю всякие техники этих тегов применения.

Комментарии 5

  1. http://django-newbie.blogspot.com/ написал:

    По поводу рефакторинга есть какие линки? P.S. А как предпросмотр сделал у тебя нет описания? Реверсижинирить, признаюсь, лень :)

    Оставлен 20 Февраль 2010 в 21:40
  2. Александр Кошелев написал:

    По поводу рефакторинга есть какие линки?

    Есть заброшенный автором тикет. Но я думаю его доделают.

    Оставлен 20 Февраль 2010 в 21:50
  3. softwaremaniacs.org/about/ написал:

    Я озадачен. А где третий способ, самый прямой: получить внутри одной view данные из разных мест и отдать их в шаблон?

    Это идеальный вариант, когда все эти "блоки" показываются например только на морде. Никакого смысла выносить логику в шаблонный тег, который не будет реюзаться, и где элементарно нет request'а, я не нахожу.

    Оставлен 20 Февраль 2010 в 21:52
  4. Александр Кошелев написал:

    Я озадачен. А где третий способ, самый прямой: получить внутри одной view данные из разных мест и отдать их в шаблон?

    Так я его вскользь упоминаю по тексту:

    Если всё-таки ваши приложения нуждаются друг в друге достаточно сильно, то нет ничего плохого в том чтобы иметь в них перекрестные импорты и заимствовать функционал (в том чиле по получению данных) так или иначе.

    только не расписываю подробно.

    Это идеальный вариант, когда все эти "блоки" показываются например только на морде. Никакого смысла выносить логику в шаблонный тег, который не будет реюзаться, и где элементарно нет request'а, я не нахожу.

    Это всё-таки не совсем тот кейс, который я тут рассматриваю.

    Оставлен 20 Февраль 2010 в 21:59
  5. Oduvan написал:

    о тут надо помнить, что контекст процессоры глобальны и добавляют данные в контекст всегда, конечно если вы во вьюхах используете RequestContext

    добовляются - они всегда, но можно ключами передавать ссылку на сункцию, и тогда вызываться она будет только в тех местах, где используется ( это чтоб не вычислять ее при каждом запросе )

    Оставлен 23 Февраль 2010 в 15:54

Пингбеки 6

  1. От Как передавать контент во все представления одновременно? 25 Январь 2011 в 18:39

    http://webnewage.org/2010/02/20/into-one-basket/

  2. От Активные зоны в общем(основном) шаблоне 26 Ноябрь 2010 в 23:07

    Каждый раз, когда тут обсуждают эту тему, всё кончается вот этой ссылкой :)

  3. От Несколько приложений на одной странице 26 Июнь 2010 в 07:12

    Это?..

  4. От Повторяющийся кусок кода во "вьюшках" 10 Май 2010 в 19:02

    Регулярно всплывает тема — http://webnewage.org/2010/02/20/into-one-basket/

  5. От переменные при наследовании шаблона 14 Апрель 2010 в 09:14

    http://webnewage.org/2010/02/20/into-one-basket/

  6. От Как организовать вывод блоков? 20 Февраль 2010 в 20:53

    Как по заказу — http://webnewage.org/2010/02/20/into-one-basket/ :-)