Django Book: загрузка шаблонов

Автор: | 02/17/2015
 

django_logo_2

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

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

Для того, что бы использовать этот API — вы должны указать фреймворку, где именно он должен искать файлы шаблонов. Сделать это можно в файле настроек вашего проекта — settings.py, который уже упоминался в первой главе, когда мы рассматривали настройку URLconf и параметр ROOT_URLCONF .

Откройте ваш файл settings.py в каталоге проекта, и найдите строку TEMPLATE_DIRS. По умолчанию она пустая, и содержит только несколько комментариев:

TEMPLATE_DIRS = (
    # Put strings here, like "/home/html/django_templates" or "C:/www/django/templates".
    # Always use forward slashes, even on Windows.
    # Don't forget to use absolute paths, not relative paths.
)

Тут указывается, где система шаблонов Django где ей искать файлы шаблонов. Найдите директорию, в которой вы хотите их хранить, и укажите её в параметре TEMPLATE_DIRS, например:

TEMPLATE_DIRS = (
    '/var/www/django/example/templates'
)

Несколько моментов, которые надо помнить:

  • Вы можете указать любую директорию, главное что бы она и файлы шаблонов в ней были доступны на чтение вашему веб-серверу. Если вы не можете придумать подходящую — то мы бы рекомендовали вам создать директорию templates в каталоге вашего проекта.

  • Если параметр TEMPLATE_DIRS содержит только один каталог — не забудьте добавить запятую после указания пути.

    Плохой вариант:

    # Missing comma!
    TEMPLATE_DIRS = (
        '/home/django/mysite/templates'
    )
    

    Хороший вариант:

    # Comma correctly in place.
    TEMPLATE_DIRS = (
        '/home/django/mysite/templates',
    )
    

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

  • Если вы пользуетесь Windows — добавляйте имя диска и используйте Unix-like косые, вместо обратных:

    TEMPLATE_DIRS = (
        'C:/www/django/templates',
    )
    
  • Лучше всего использовать полные пути от корня вашей файловой системы. Но если вы хотите иметь более гибкую систему — вы можете использовать тот факт, что файл настроек Django — это простой файл Python, и вы можете указать путь к директории шаблонов динамически, например так:

    import os.path
    
    TEMPLATE_DIRS = (
        os.path.join(os.path.dirname(__file__), 'templates').replace('\','/'),
    )
    

    В этом примере используйется специальная переменная Python __file__, которая указывает на файл, из которого она вызывается. Тут берётся путь к директории, в которой находится файл settings.py (os.path.dirname), к нему добавляется templates с помощью кросс-платформенной функции (os.path.join), затем все обратные слеши меняются на прямые (на случай использования под Windows).

    Так как вы теперь немного в курсе использования кода Python в файле настроек — мы должны предупредить вас, что необходимо быть очень осторожным и избегать ошибок Python в нём. Если вы допустите синтаксическую или другую ошибку — ваш сайт упадёт.

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

Вернёмся к нашему представлению current_datetime, и изменим его так:

from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse, Http404
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('timetemplate.html')
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

В этом примере мы используем функцию django.template.loader.get_template() вместо того, что бы загружать файл шаблона вручную. Функция get_template() принимает имя шаблона первым аргументом, определяет где хранятся шаблоны в файловой системе, открывает этот файл и возвращает скомилированный объект Template.

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

Что бы определить расположение файла шаблона в файловой системе, get_template() совмещает директорию шаблонов из настройки TEMPLATE_DIRS и имя шаблона, переданное этой функции. Например, если ваш TEMPLATE_DIRS указан в /var/www/django/example/templates, то функция get_template() будет искать файл /var/www/django/example/templates/timetemplate.html.

Если get_template() не сможет найти файл с заданным именем — она вызове исключение TemplateDoesNotExist. Вот как он выглядит:

Django TemplateDoesNotExist

Эта страница схожа с той, которую мы рассматривали в статье Django Book: страница ошибки Django, с одним дополнительным полем информации — блок «Template-loader postmortem». В нём описывается — какой именно шаблон Django пытался загрузить и детали неудачной попытки (например — «File does not exist»). Она окажет неоценимую помощь во время поиска причины, по которой шаблон не загружается.

render()

Мы показали вам, как загрузить шаблон, заполнить Context и вернуть объект HttpResponse с результатом рендеринга шаблона. Мы оптимизировали этот процесс с использованием  функции get_template() вместо того, что указывать файл в коде напрямую. Однако, нам всё ещё требуется много печатать, что бы выполнить все эти действия.

Django предоставляет возможность уменьшить все эти действия — загрузить шаблон, выполнить рендеринг и вернуть HttpResponse — всего одной строкой кода.

Эта возможность — функция с именем render() из модуля django.shortcuts. В основном вы будете пользоваться именно ей, вместо того, что бы выполнять все эти действия вручную — по крайней мере, если ваш работодатель не платит вам за количество строк кода, который вы пишете.

Вот наша функция current_datetime, переписанная с использованием render():

from django.shortcuts import render
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    return render(request, 'timetemplate.html', {'current_date': now})

Чувствуете разницу? Давайте посмотрим  какие изменения мы сделали:

  • Мы больше не выполняем импорт get_template, Template, Context и HttpResponse. Вместо этого мы импортируем django.shortcuts.renderimport datetime остаётся.
  • Внутри функции current_datetime мы оставляем вычисление значения переменной now, но убираем загрузку шаблона, создание контекста, рендеринг шаблона и создание HttpResponse — теперь всем этим занимается функция render().

Первый аргумент для render() — это request, второй — имя шаблона. Третий аргумент, если указан — это словарь для создания контекста этого шаблона. Если его не указать — render() будет использовать пустой словарь.

Вложенные каталоги в get_template()

Может быть неудобно хранить все ваши шаблоны в одной директории. Возможно, вы захотите поместить некоторые шаблоны во вложенные каталоги вашей директории templates — и это можно сделать. Мы даже рекомендуем вам так поступать — некоторые более продвинутые возможности Django будут ожидать такого.

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

t = get_template('dateapp/current_datetime.html')

Так как render() — это лишь маленькая обёртка вокруг get_template() — вы можете сделать то же со вторым аргументом ей, например:

return render(request, 'dateapp/current_datetime.html', {'current_date': now})

Нет никаких ограничений на глубину вложенности каталогов.

Примечание

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

Тег шаблонов include

Теперь, когда мы в целом разобрались с системой загрузки шаблонов, мы можем рассказать вам о встроенном теге {% include %}, который даст вам дополнительные преимущества. Он позволяет включать содержимое одного шаблона в другой. Аргументом ему передаётся имя шаблона, который необходимо включить в основной шаблон, а имя может быть задано либо с помощью переменной — либо в виде строки, заключённой в кавычки. Каждый раз, когда вам приходится использовать один и тот же код в шаблонах — старайтесь пользоваться {% include %}.

В этих двух примерах выполняется добавление шаблона nav.html. Оба примера одинаковы — вы можете использовать как одинарные, так и двойные кавычки:

{% include 'nav.html' %}
{% include "nav.html" %}

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

{% include 'includes/nav.html' %}

В этом примере — подключается шаблон, чьё имя хранится в переменной template_name:

{% include template_name %}

Как и в функции get_template() имя файла определяется путём совмещения значения TEMPLATE_DIRS и имени шаблона.

Вложенные шаблоны работают с теми же контекстами, с которыми создавался «вышестоящий» шаблон. Предположим, что у нас есть два шаблона:

# mypage.html

<html>
<body>
{% include "includes/nav.html" %}
<h1>{{ title }}</h1>
</body>
</html>

и:

# includes/nav.html

<div id="nav">
    You are in: {{ current_section }}
</div>

При рендеринге mypage.html с контекстом, содержащим переменную current_section — она будет доступна из вложенного шаблона.

Если при использовании тега {% include %} заданный шаблон не будет найден — Django поступит следующим образом:

  • Если DEBUG включен — будет выдана ошибка TemplateDoesNotExist на странице ошибок Django;
  • Если DEBUG выключен — никаких сообщений не будет, а на месте тега будет пустое место.

Продолжение и последняя часть второй главы — Django Book: наследование в шаблонах.