Django шаблоны наследование

Наследование шаблона django — views.py для нескольких дочерних шаблонов

Я пытаюсь создать base.html и загрузить на базу несколько дочерних шаблонов с именем «nav.html», «contents.html» и «footer.html». Я хочу, чтобы все три дочерние шаблоны были загружены на странице base.html. Когда я посещаю /base.html.

Прямо сейчас мой view.py выглядит так:

Который не загружает ни один из трех дочерних шаблонов, и если я загружаю один из дочерних шаблонов, например

Django шаблоны наследование

Прочитал статью «Фрагментарное кэширование в MVC веб-фреймворках». Статья описывает проблему кеширования фрагмета отображения, а именно проблему полного разделения контроллера и отображения — контроллер отрабатывает полностью до вызова отображения. Если в отображении мы кешируем фрагмент, это ничего не меняет — контроллер-то уже отработал! В статье описан способ этого избежать: сделать запрос данных «ленивым».

Начав писать, как это должно быть сделано правильно, решил написать, как устроены шаблоны Django, чтобы не-джанговодам тоже было понятно.

Как это сделано в Django?

Структура шаблонов Django

Управляющими элементами шаблонов Django являются переменные, фильтры и теги.

При рендеринге шаблона переменные заменяются на свое значение, вычисленное в контексте вызова. Синтаксис — двойные фигурные скобки — например: << title >> .

Фильтры служат для простых преобразований переменных. Синтаксис — переменная|имя_фильтра:»параметры» . Фильтр может встречаться как в переменных, так и в качестве параметра тега. Например: << title|lowercase >> .

При рендеринге шаблона теги, грубо говоря, заменяются на результаты работы ассоциированной с этим тегом функции на питоне. Синтаксис: <% тег параметры %>, например: <% url blog_article slug=article.slug %>.

Программист может написать свои фильтры и теги, но об этом позже.

Кроме того, есть три специальных тега: include , block и extend . Тег include подставляет запрошенный шаблон (отрендеренный в текущем контексте).

Все выше перечисленное тривиально и в той или иной форме есть в любом движке шаблонов. Теперь перейдем к особенностям Django: на тегах block и extend строится наследование шаблонов. Остановимся на них подробнее.

Наследование шаблонов

Основная фишка шаблонов Django — наследование. Шаблон может расширять (уточнять) поведение родительского шаблона.

Любой участок шаблона может быть обернут в блочный тег (естественно, что тег не может начинаться перед, а заканчиваться внутри цикла). Блоку дается имя. Например:

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

Получается мощный механизм, практически исключающий необходимость повторения частей шаблонов. Вкратце это описано в документации (см. ссылки в конце статьи). Давайте разберем реальный пример.

Пример наследования шаблонов

Допустим, мы хотим сделать сайт, содержащий простые страницы и блог.

От верстальщика мы получили макет страницы, содержащий:

  • шапку (логотип, заголовок страницы, меню);
  • тело страницы;
  • и «подвал» с информацией о правах распространения.

Вот как это выглядит:

Для всех указанных элементов мы создаем соответствующие блочные теги.

Простая страница ложится в этот макет — у нее есть только заголовок и тело.

Теперь перейдем к блогу. В блоге хотелось бы добавить правую колонку для вывода списка тегов и последних статей. Возможно мы захотим добавить правую колонку к каким-нибудь другим страницам сайта. Чтобы избежать копирования «двухколоночности», вынесем ее в отдельный шаблон, переопределив тело страницы у базового.

В блоге будет несколько типов страниц:

  • список статей;
  • статья;
  • список тегов;
  • список статей, у которых есть определенный тег;
  • пр.

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

Теперь приведем примеры внутренних страниц блога (все они наследуются от базовой страницы блога).

Список статей, у которых есть определенный тег:

В данном случае, мы воспользовались еще одной хитростью. Ведь этот список ничем не отличается от простого списка статей — он просто отфильтрован по дополнительным параметрам. Поэтому мы унаследовали его от списка статей и при перекрытии тела использовали тег << block.super >> — вывести все содержимое родительского блока.

Как видно, каждый шаблон очень конкретен и отвечает только за свою функциональность. Ему нет необходимости знать о всей странице в целом.

Поклонники других шаблонных систем скажут, что для приведенного примера наследование не нужно. Действительно, то же самое можно реализовать, используя теги подстановки ( include , ssi ). Вот только логика и структура этих включений будет намного запутаннее. Получится, что статья должна знать, какие блоки будут на ее странице, и предоставлять данные для всех этих блоков. Тут на помощь приходит еще одна особенность Django — пользовательские теги.

Пользовательские теги

В нашем примере на странице статьи блога есть 7 блоков. Два из них — логотип и copyright — не нуждаются в данных. Для остальных пяти контроллеру необходимо предоставить шаблону данные. А именно:

  • заголовок статьи;
  • меню;
  • тело статьи;
  • список тегов;
  • последние статьи.

Блоков могло быть намного больше, но непосредственное отношение к статье имеют только заголовок и тело статьи. Зачем статье знать, какие данные нужны этим блокам, откуда и как их получить? Абсолютно незачем — это не ее задача. Django предлагает нам следующее решение этой проблемы.

Смотрите так же:  Отдел опеки курск

Для каждого из блоков мы можем написать свой тег, состоящий из мини-контроллера и шаблона. Контроллер знает, как получить данные, шаблон — как отобразить. В том месте, где нам необходим блок, мы вставляем его тег — и все! Например, можно вставить список тегов и последних статей на главную страницу. Главной странице нет необходимости что-либо знать о структуре нашего блога — только факт наличия и имена тегов, реализуемых блогом.

Вот пример тега для вывода списка последних статей в блоге:

Еще одним приемуществом такого подхода является то, что данные запрашиваются непосредственно при вставке тега. Если кэшировать несколько тегов, то будут кэшированы результаты их работы — повторно данные запрашиваться не будут! И не надо изобретать велосипеды, как тут 😉

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

Что такое extends в Pug (Jade)?

extends описывает шаблон (предок или раскладку), и выхлоп будет обёрнут в этот базовый шаблон, при этом текущий шаблон может заменить любые блоки в шаблоне-предке.

Наследование шаблонов может иметь любую глубину.

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

Ну и для внедрения раскладок (layouts).

Воспринимайте просто как наследование, где потомок может переопределять блоки предка.

Архитектура шаблонов Django

Я решил поделиться структурой, которую я использую для шаблонов. Я ее использую как в новых проектах, так и при реструкторизации уже существующих приложений. Этот процесс направлен на получение повторно используемых приложений с сохранением гибкости. Эта структура родилась постепенно в ходе многих тысяч часов работы с шаблонами и является удобной для меня. Ваши предпочтения могут отличаться. И вы же знакомы с организацией шаблонов в поддиректориях приложений? Верно?

Перед тем, как начать, я хотел бы показать одно из моих Django приложений с шаблонами. Gazette — приложение-блог (да, еще одно), над которым я работал и которое я буду использовать в качестве примера в данной статье.

Знайте ваши шаблоны

Первый принцип процесса разработки моих шаблонов пришел прямиком из The Zen of Python:

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

Расширяемые шаблоны

Расширяемые шаблоны формируют основную структуру ваших HTML страниц. Это шаблоны, основная цель которых быть расширенными другими шаблонами с помощью тега <% extends %>. Чтоб избежать перемешивания расширяемых шаблонов с шаблонами страниц я принял за правило никогда не вызывать расширяемые шаблоны напрямую из кода представлений (view) и никогда не расширять шаблоны, которые не являются расширяемыми. По соглашению о наименовании, я начинаю все свои расширяемые шаблоны с двойного подчеркивания: __base.html. Самым важным шаблоном является __base.html, который содержит скелет HTML для всех ващих страниц. Обычно этот шаблон делается минимальным. У меня он обычно выглядит следующим образом:

Как вы можете заметить, я использую Bootstrap для новых проектов, но этот же подход справедлив и для проектов без Bootstrap. Когда я пишу базовый шаблон, я стараюсь делать это осторожно. Я думаю: “Когда другой программист (включая меня через полгода) прийдет в этот проект и ему понадобится создать новый шаблон, могу ли я быть уверенным, что он сможет сделать любой шаблон не меняя базовый”. Результат оборачивается большим кол-вом тегов <% block %>, которые можно переопределить или расширить при необходимости. О теге <% block %>поговорим чуть позже. Если у вас очень простое приложение, то вам многое не нужно из того, что может предоставить __base.html для вашей структуры. И вы можете перейти к написанию шаблонов страниц и включаемых шаблонов. Но если ваше приложение достаточно сложное, то вам может понадобиться создать несколько различных структурных шаблонов (layout templates). В Gazette я использую три структурных шаблона, имена которых начинаются с __l_ (и снова их назначение определяется мгновенно из названия): __l_single_col.html, __l_right_sidebar.html, __l_left_sidebar.html. Я храню их в одной директории с остальными шаблонами согласно принципу:

Плоское лучше, чем вложенное

С другой стороны, если вам необходимо более трех шаблонов, то вы можете поместить их в поддиректорию layouts/. Когда я работаю над проектов с очень сложной сеткой, то часто создаю такую поддиректорию с шаблонами: layouts/100.html, layouts/25_75.html, layouts/50_50.html и т.д. Например один из моих структурных шаблонов, __l_right_sidebar.html :

Это очень простой шаблон — настолько простой, что может показаться, что он и не нужен. Но, поверьте мне, этот маленький шаблон предоставляет множество преимуществ при создании ваших шаблонов страниц.

Включаемые шаблоны

Включаемые шаблоны — это шаблоны, которые планируете включать в шаблоны страниц с помощью тега <% include %>. Как и с расширяемыми шаблонами я строго слежу за применением включаемых шаблонов. Вы можете рассматривать их и как некие блоки, из которых строится ваш сайт, собираясь в шаблоне страницы. Есть две основные причины сделать часть вашего сайте включаемым:

  • вы планируете использовать этот модуль повторно на многих страницах и вы хотите сохранить чистоту кода.
  • вы хотите предоставить другому разработчику возможность переопределить этот модуль в вашем приложении без вмешательства в структуру вашего шаблона.
Смотрите так же:  Заявление в арбитражный суд о возмещении убытков

И как всегда, разрабатывайте продуманно: будьте готовы, что другие разработчики (включая вас через полгода) захотят изменить или переопределить ваши шаблоны так, как вы даже не можете представить. Но следите за балансом. Слишком мало включаемых блоков дает мало возможностей для модификации, что раздражает, а слишком много — создает спагетти код, в котором сложно ориентироваться.

Шаблоны страниц

Шаблоны страниц — ядро вашего приложения. Но если вы перед этим создали несколько хороших расширяемых шаблонов и возможно даже несколько включаемых, то вы заметите насколько просто связать все вместе. Это просто клей, который связывает ваши шаблоны вместе. Ваши шаблоны страниц — это именно то, что вызывается из кода представлений (views). Они гармонично связывают расширяемые и включаемые вместе. Например, article_detail.html :

Как вы видете, эта страница использует __l_right_sidebar.html. Главная колонка содержит полный текст статьи. Боковая колонка содержит список тегов и список категорий. Этот шаблон настолько легко читаем, насколько возможно. Я упоминал, что выделение отдельного уровня шаблонов структуры приведет к увеличению возможностей и это так. Если вы решите, что список тегов и категорий лучше расположить в левой колонке, то вам достаточно будет лишь поменять первую строку шаблона article_detail.html на:

и содержимое переместится именно туда, куда надо. Дальше больше. Если другой разработчик предпочитает структуру 75% — 25% вместо 66% — 33%, которую использую я в __l_right_sidebar.html, то от может переопределить шаблон:

и все страницы, которые его используют, изменятся нужным образом. Согласно принятому соглашению о названиях, когда это возможно, я стараюсь следовать соглашению об именях, принятому в Django generic views, т.е. _list.html, _detail.html, _form.html и т.д. Если шаблон не попадает под эти категории, то просто старайтесь давать ему название, четко определяющее то представление (view), к которому он относится.

Система наследования шаблонов в Django удивительно мощная и тут очень просто увлечься. Я часто вижу как в базовых шаблонах (т.е. расширяемых) разработчики пишут контент по-умолчанию в блоках:

предполагая, что он будет переопределен при расширении данного шаблона в других страницах. Я хочу предостеречь вас от использования этого подхода, т.к. он смешивает ваши расширяемые шаблоны с шаблонами страниц и становиться сложнее находить то месно, которое нужно изменить в каждом конкретном случае. (“Где это ? В frontpage.html или в base.html?”). Вместо этого рассматривайте содержимое блоков в расширяемом шаблоне как текст, который может быть использован в разных шаблонах страниц с помощью << block.super >>. Простейший пример — это элемент страницы . Я часто вижу следующую структуру в базовом шаблоне:

Это не только кажется мне не элегантным (Куда нужно поместить разделитель между заголовком страницы и заголовком сайта, т.е. “Заголовок страницы | Заголовок сайта” — в базовый шаблон или в шаблон страницы? А как быть со страницами, которым не нужен заголовок ?), но и ограничивает разработчика в том, что он может сделать без внесения изменений в базовый шаблон. И я гораздо реже вижу более простую структуру:

Аналогично с css или другими включениями:

Это запутаннее и избыточнее, чем простое:

Позже в ваших шаблонах страниц вы можете добавить другие стили следующим образом:

Аналогично и с заголовком:

Это, на мой взгляд, элегантнее — блоки лучше названы, их меньше, они более точно выражают структуру — и если вы захотите отказаться от заголовка по-умолчанию или стилей по-умолчанию, не отказываясь от всего базового шаблона, вы свободно можете это сделать. << block.super >> — очень мощная штука. Используйте ее внимательно.

Несколько заметок на последок

Интернационализация

Пожалуйста, используйте интернационализацию ваших шаблонов. Особенно, если вы пишите приложения для повторного использования. Это очень просто с использованием тегов <% trans %>и <% blocktrans %>.

Довольно долгое время я писал ужасный код наподобее этого:

До тех пор, пока не заметил [удобный синтаксис], который доступен с версии Django 1.3:

Для дополнительной инкапсуляции, попробуйте передать в ваши включаемые шаблоны эксклюзивный контекст:

Закрывающиеся теги HTML и язык шаблона Django по своей природе — вложенный, что иногда снижает читаемость.

Особенно при работе с большими блоками. Язык шаблонов Django имеет дополнительный необязательный синтаксис, который помогает отслеживать ваши блоки:

А в других случаях? Я часто использую комментарии для имитации этого для других тегов шаблона и HTML:

что, как я заметил, сильно повышает наглядность кода.

Обрабатывайте HTML и теги шаблона одинаково

Я всегда считал, что отступы для HTML тегов должны обрабатываться отдельно от отступов тегов шаблона. Возможно это ошибочное мнение было основано на том, что структура результирующего HTML кода имеет большее значение. Это все приводило к тому, что большое количество кода имело вид:

Этот код можно сделать более читабельным, если рассматривать теги HTML и теги шаблона как равноправные:

Django Book: наследование в шаблонах

Предыдущая часть.

Наши предыдущие примеры шаблонов были небольшими фрагментами HTML-кода, однако в реальной ситуации вы будете использовать Django для создания больших страниц. Отсюда возникает один из наиболее существенных вопросов веб-разработки – как уменьшить количество повторяющегося и избыточного кода в общих частях страниц, таких как навигация по сайту?

Классическое решение этой проблемы заключается в использовании инклюдов (includes), или “вложений” – особых директив в коде HTML, которые позволяют включать одну HTML-страницу в код другой. Действительно, Django поддерживает такой подход с помощью тега шаблона <% include %>, который мы рассмотрели в статье Django Book: глава 2 – загрузка шаблонов. Но имеется и другой, более предпочтительный, способ решения, который называется Наследование шаблонов.

Смотрите так же:  306 приказ мо рф

Кратко говоря, наследование позволяет вам построить базовый “скелет” шаблона, который содержит все части вашего сайта и определяет блоки, которые могут быть перезаписаны “дочерними” шаблонами.

Давайте рассмотрим пример реализации этого, создав более полный шаблон для нашего преставления current_datetime , отредактировав файл timetemplate.html :

Он выглядит достаточно хорошо, но что будет, если мы захотим создать шаблон для другого представления, например – hours_ahead из статьи Django Book: третье представление – динамические URL-ы? Если мы опять хотим создать красивый и валидный HTML-шаблон – нам придётся делать что-то вроде такого:

Очевидно, что мы дублируем много HTML-кода. Представьте, что у нас есть типичный сайт, с панелью навигации, несколько CSS, возможно JavaScript – нам придётся добавлять весь избыточный код в каждый шаблон.

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

Например, вы могли бы сохранить заголовок страницы в шаблоне с именем header.html :

А окончание страницы – в файле footer.html :

С использованием инклюдов такое реализовать очень просто. Однако, тут есть несколько проблем. Например, обе страницы используют заголовок страницы –

My helpful timestamp site

в заголовок – нам придётся включить туда и , и это не позволит нам настраивать страницы.

Система наследования в шаблонах Django решает эту проблему. Вместо определения того, как части страницы будут общими – вы можете определить какие из них будут разными.

Первый шаг – определить базовый шаблон – скелет страницы, который потом будет заполнен дочерними шаблонами. Вот пример нашего текущего шаблона:

Этот шаблон мы назовём base.html , и он описывает простой HTML-скелет документа, который мы будем использовать для всех страниц сайта. Далее уже наши дочерние шаблоны будут перезаписывать, добавлять или удалять содержимое блоков. Сохраните его в вашей директории templates с именем base.html .

Тут мы использовали тег шаблона, который вы раньше не встречали – <% block %>. Всё, что он делает – это указывает системе шаблонов, что дочерний шаблон может перезаписать этот участок шаблона.

Теперь, мы можем отредактировать наш шаблон timetemplate.html , что бы использовать эту возможность:

Теперь, давайте создадим шаблон для представления hours_ahead (мы предлагаем вам самим изменить само представление, что бы оно использовало систему шаблонов, вместо кода непосредственно в коде представления). Вот как он может выглядеть:

Правда – замечательно? Каждый шаблон только свой уникальный код, больше нет никакой избыточности. Если вам необходимо сделать изменения, касающиеся всего сайта – достаточно отредактировать файл base.html , и все шаблоны немедленно их отобразят.

Вот как это работает. Когда загружается шаблон timetemplate.html , система шаблонов встречает тег <% extends %>, который означает что это дочерний шаблон. Система сразу же подгружает родительский шаблон, в данном случае это base.html .

Далее, система находит три тега <% block %>в шаблоне base.html , и заменяет их значение блоками из дочерней страницы. Таким образом, мы заменяем блоки <% block title %>и <% block content %>.

Заметьте, что так как дочерний шаблон не определяет блок footer – система шаблонов использует значение из родительского шаблона. Содержимое блока <% block %>в родительском шаблоне всегда используется как “запасной”, если не указано ничего в дочернем.

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

Вы можете использовать любое количество уровней наследования. Обычно используют трёх-уровневый подход:

  1. Базовый шаблон base.html , который описывает общий вид вашего сайта. Он меняется редко – или даже вообще никогда.
  2. Создаются base_SECTION.html шаблоны, один для каждой “секции” сайта (например – base_photos.html и base_forum.html ). Эти шаблоны дополняют base.html и содержат в себе специфичные для раздела стили и дизайн.
  3. Создаются отдельные шаблоны для каждого типа страницы, таких как форум или фотогалерея, которые дополняют шаблон секции.

Такой подход позволяет максимально использовать один и тот же код, и упрощает добавление объектов в общие части сайта, такие как панель навигации.

Вот несколько указаний для работы с наследованием шаблонов:

Как сделать наследование шаблонов?

Возможно ли в Yii2 сделать наследование шаблонов как в Django? Я знаю что по умолчанию тут уже есть наследование всех шаблонов от layout, но мне надо отнаследовать шаблон от другого шаблона наследованного от layout. Т.е. layout -> template1 -> template2. Еще мне говорили что такие вещи можно как-то решить с помощью виджетов, но я не понимаю как в данном случае это можно сделать.

Идея простая: мне на странице нужны вкладки. Я использую вот эти (хотя есть и стандартные в Yii — я знаю). Пытался разобраться с аяксовой подгрузкой контента, но бросил эту затею — слишком сложно. Мне надо чтобы каждая вкладка была доступна по ссылке вида «/edit?step=1». Т.е. каждая вкладка — шаг редактирования модели.