Spec-Zone.ru › Django 1.9

Шаблоны

В качестве веб-фреймворка, Django нуждается в удобном способе динамически генерировать HTML. Наиболее распространённый подход использует шаблоны. Шаблон содержит статические части желаемого HTML-вывода, а также специальный синтаксис, описывающий, как будет вставлено динамическое содержимое. Для практического примера создания HTML-страниц с помощью шаблонов, см. Урок 3.

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

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

Язык шаблонов Django — собственная система шаблонов Django. До Django 1.8 он был единственным доступным встроенным вариантом. Это хорошая библиотека шаблонов, хотя и довольно однозначная, и имеет несколько особенностей. Если у вас нет веских причин выбирать другой бэкенд, вы должны использовать DTL, особенно если вы пишете подключаемый модуль и планируете распространять шаблоны. Приложения contrib Django, включающие шаблоны, такие как django.contrib.admin, используют DTL.

По историческим причинам, как общая поддержка движков шаблонов, так и реализация языка шаблонов Django находятся в пространстве имен django.template.

Поддержка движков шаблонов

Поддержка нескольких движков шаблонов и настройка TEMPLATES были добавлены в Django 1.8.

Настройка

Движки шаблонов настраиваются с помощью настройки TEMPLATES. Это список конфигураций, по одной для каждого движка. Значение по умолчанию — пустое. Значение settings.py, сгенерированное командой startproject, определяет более полезное значение:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [],
        'APP_DIRS': True,
        'OPTIONS': {
            # ... some options here ...
        },
    },
]

BACKEND — это путь к классу движка шаблонов в Python, реализующему API бэкенда шаблонов Django. Встроенные бэкэнды — django.template.backends.django.DjangoTemplates и django.template.backends.jinja2.Jinja2.

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

  • DIRS определяет список каталогов, в которых движок должен искать файлы шаблонов в порядке поиска.
  • APP_DIRS указывает, должен ли движок искать шаблоны внутри установленных приложений. Каждый бэкенд определяет стандартное имя подкаталога внутри приложений, где должны храниться его шаблоны.

Хотя это редко встречается, можно настроить несколько экземпляров одного и того же бэкенда с разными опциями. В этом случае вы должны определить уникальное NAME для каждого движка.

OPTIONS содержит настройки, специфичные для бэкенда.

Использование

Модуль django.template.loader определяет две функции для загрузки шаблонов.

get_template(template_name, dirs=_dirs_undefined, using=None) [source]

Эта функция загружает шаблон с заданным именем и возвращает объект Template.

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

get_template() пытается использовать каждый движок шаблонов по порядку, пока один из них не преуспеет. Если шаблон не найден, он вызывает TemplateDoesNotExist. Если шаблон найден, но содержит неверный синтаксис, он вызывает TemplateSyntaxError.

Как ищутся и загружаются шаблоны, зависит от бэкенда и конфигурации каждого движка.

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

Устарело начиная с версии 1.8: Параметр dirs устарел.

Параметр using был добавлен.

get_template() возвращает зависящий от бэкенда Template вместо django.template.Template.

select_template(template_name_list, dirs=_dirs_undefined, using=None) [source]

select_template() аналогичен get_template(), за исключением того, что он принимает список имён шаблонов. Он пытается использовать каждое имя по порядку и возвращает первый найденный шаблон.

Устарело начиная с версии 1.8: Параметр dirs устарел.

Параметр using был добавлен.

select_template() возвращает зависящий от бэкенда Template вместо django.template.Template.

Если загрузка шаблона завершается неудачей, могут быть вызваны следующие два исключения, определённые в django.template.

exception TemplateDoesNotExist(msg, tried=None, backend=None, chain=None) [source]

Это исключение возникает, когда шаблон не найден. Оно принимает следующие необязательные аргументы для заполнения справочной информации по шаблону на странице отладки:

backend
Экземпляр движка шаблонов, из которого произошло исключение.
tried
Список источников, которые были проверены при поиске шаблона. Он отформатирован как список кортежей, содержащих (origin, status), где origin — объект типа «источник», а status — строка с причиной того, почему шаблон не был найден.
chain
Список промежуточных исключений TemplateDoesNotExist, возникших при попытке загрузить шаблон. Это используется функциями, такими как get_template(), которые пытаются загрузить заданный шаблон из нескольких движков.

Аргументы backend, tried, и chain были добавлены.

exception TemplateSyntaxError(msg) [source]

Это исключение возникает, когда шаблон был найден, но содержит ошибки.

Объекты Template, возвращаемые get_template() и select_template(), должны предоставлять метод render() со следующим сигнатурой:

Template.render(context=None, request=None)

Рендерит данный шаблон с заданным контекстом.

Если context предоставлен, он должен быть dict. Если он не предоставлен, движок рендерит шаблон с пустым контекстом.

Если request предоставлен, он должен быть HttpRequest. Тогда движок должен сделать его, а также маркер CSRF, доступными в шаблоне. Как это достигается, зависит от каждого бэкенда.

Вот пример алгоритма поиска. Для этого примера настройка TEMPLATES:

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [
            '/home/html/example.com',
            '/home/html/default',
        ],
    },
    {
        'BACKEND': 'django.template.backends.jinja2.Jinja2',
        'DIRS': [
            '/home/html/jinja2',
        ],
    },
]

Если вы вызываете get_template('story_detail.html'), вот файлы, которые Django будет искать в порядке:

  • /home/html/example.com/story_detail.html ('django' движок)
  • /home/html/default/story_detail.html ('django' движок)
  • /home/html/jinja2/story_detail.html ('jinja2' движок)

Если вы вызываете select_template(['story_253_detail.html', 'story_detail.html']), вот что Django будет искать:

  • /home/html/example.com/story_253_detail.html ('django' движок)
  • /home/html/default/story_253_detail.html ('django' движок)
  • /home/html/jinja2/story_253_detail.html ('jinja2' движок)
  • /home/html/example.com/story_detail.html ('django' движок)
  • /home/html/default/story_detail.html ('django' движок)
  • /home/html/jinja2/story_detail.html ('jinja2' движок)

Когда Django находит шаблон, который существует, он прекращает поиск.

Подсказка

Вы можете использовать select_template() для гибкой загрузки шаблонов. Например, если вы написали новостную статью и хотите, чтобы некоторые статьи имели пользовательские шаблоны, используйте что-то вроде select_template(['story_%s_detail.html' % story.id, 'story_detail.html']). Это позволит вам использовать пользовательский шаблон для отдельной статьи со спасочным шаблоном для статей, у которых нет пользовательских шаблонов.

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

Сделайте это для собственного спокойствия. Хранение всех шаблонов в корневом уровне одного каталога становится громоздким.

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

get_template('news/story_detail.html')

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

  • /home/html/example.com/news/story_detail.html ('django' движок)
  • /home/html/default/news/story_detail.html ('django' движок)
  • /home/html/jinja2/news/story_detail.html ('jinja2' движок)

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

render_to_string(template_name, context=None, context_instance=_context_instance_undefined, request=None, using=None) [source]

render_to_string() загружает шаблон, как get_template() и немедленно вызывает его метод render(). Она принимает следующие аргументы.

template_name
Имя шаблона для загрузки и рендеринга. Если это список имён шаблонов, Django использует select_template() вместо get_template() для поиска шаблона.
context

Словарь dict для использования в качестве контекста шаблона для рендеринга.

Аргумент context раньше назывался dictionary. Это имя устарело в Django 1.8 и будет удалено в Django 1.10.

context теперь необязательно. Если он не указан, будет использован пустой контекст.

context_instance

Экземпляр Context или его подкласс (например, экземпляр RequestContext) для использования в качестве контекста шаблона.

Устарело начиная с версии 1.8: Аргумент context_instance устарел. Используйте context и, при необходимости, request.

request

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

Аргумент request был добавлен.

Пример использования:

from django.template.loader import render_to_string
rendered = render_to_string('my_template.html', {'foo': 'bar'})

См. также render() — короткую запись, которая вызывает render_to_string() и передает результат в HttpResponse, пригодный для возврата из представления.

Наконец, вы можете использовать настроенные движки напрямую:

engines

Движки шаблонов доступны в django.template.engines:

from django.template import engines

django_engine = engines['django']
template = django_engine.from_string("Hello {{ name }}!")

Ключ поиска — 'django' в этом примере — это NAME движка.

Встроенные движки

class DjangoTemplates [source]

Установите BACKEND в 'django.template.backends.django.DjangoTemplates' для настройки движка Django шаблонов.

Когда APP_DIRS имеет значение True, движки DjangoTemplates ищут шаблоны в подкаталоге templates установленных приложений. Это общее имя сохранено для обратной совместимости.

Движки DjangoTemplates принимают следующие OPTIONS:

  • 'allowed_include_roots': список строк, представляющих разрешённые префиксы для тега {% ssi %} шаблона. Это мера безопасности, чтобы авторы шаблонов не могли получить доступ к файлам, к которым они не должны иметь доступ.

    Например, если 'allowed_include_roots' равно ['/home/html', '/var/www'], то {% ssi /home/html/foo.txt %} будет работать, но {% ssi /etc/passwd %} — нет.

    По умолчанию пустой список.

    Устарело начиная с версии 1.8: allowed_include_roots устарел, так как тег {% ssi %} устарел.

  • 'context_processors': список точечных путей Python к вызываемым функциям, используемым для заполнения контекста при рендеринге шаблона с запросом. Эти вызываемые функции принимают объект запроса в качестве аргумента и возвращают dict элементов для объединения в контекст.

    По умолчанию пустой список.

    См. RequestContext для получения дополнительной информации.

  • 'debug': логическое значение, включающее/выключающее режим отладки шаблона. Если это True, то на странице с ошибкой будет отображаться подробный отчёт о любом исключении, возникшем во время рендеринга шаблона. Этот отчёт содержит соответствующий фрагмент шаблона с выделенной строкой.

    По умолчанию значение DEBUG настройки.

  • 'loaders': список точечных путей Python к классам загрузчика шаблонов. Каждый класс Loader знает, как импортировать шаблоны из определенного источника. Дополнительно может быть использован кортеж вместо строки. Первый элемент кортежа должен быть именем класса Loader, а последующие элементы передаются в Loader во время инициализации.

    Значение по умолчанию зависит от значений DIRS и APP_DIRS.

    См. Типы загрузчиков для получения подробных сведений.

  • 'string_if_invalid': строковое значение, которое система шаблонов должна использовать для неверных (например, неверно написанных) переменных.

    По умолчанию пустая строка.

    См. Как обрабатываются неверные переменные для получения подробных сведений.

  • 'file_charset': кодировка, используемая для чтения файлов шаблонов на диске.

    По умолчанию значение FILE_CHARSET.

  • 'libraries': словарь меток и точечных путей Python модулей тегов шаблонов для регистрации в движке шаблонов. Это может быть использовано для добавления новых библиотек или предоставления альтернативных меток для существующих. Например:

    OPTIONS={
        'libraries': {
            'myapp_tags': 'path.to.myapp.tags',
            'admin.urls': 'django.contrib.admin.templatetags.admin_urls',
        },
    }
    

    Библиотеки могут быть загружены путем передачи соответствующего ключа словаря в тег {% load %}.

  • 'builtins': список точечных путей Python модулей тегов шаблонов для добавления в встроенные. Например:

    OPTIONS={
        'builtins': ['myapp.builtins'],
    }
    

    Теги и фильтры из встроенных библиотек могут быть использованы без предварительного вызова тега {% load %}.

Аргументы libraries и builtins были добавлены.

class Jinja2 [source]

Требует, чтобы Jinja2 был установлен:

$ pip install Jinja2

Установите BACKEND на 'django.template.backends.jinja2.Jinja2' для настройки движка Jinja2.

Когда APP_DIRS имеет значение True, движки Jinja2 ищут шаблоны в подкаталоге jinja2 установленных приложений.

Наиболее важной записью в OPTIONS является 'environment'. Это строка с точкой в виде пути к Python-вызову, возвращающему среду Jinja2. По умолчанию это 'jinja2.Environment'. Django вызывает эту функцию и передает другие параметры в качестве ключевых аргументов. Кроме того, Django добавляет значения по умолчанию, отличающиеся от значений по умолчанию Jinja2 для некоторых параметров:

  • 'autoescape': True
  • 'loader': загрузчик, настроенный для DIRS и APP_DIRS
  • 'auto_reload': settings.DEBUG
  • 'undefined': DebugUndefined if settings.DEBUG else Undefined

Конфигурация по умолчанию преднамеренно минимальна. Если шаблон отображается с запросом (например, при использовании render()), бэкэнд Jinja2 добавляет глобальные переменные request, csrf_input, и csrf_token в контекст. Помимо этого, этот бэкэнд не создаёт среду с джанго-специфичными функциями. Он не знает о процессорах контекста, фильтрах и тегах Django. Для использования джанго-специфических API необходимо настроить их в среде.

Например, вы можете создать myproject/jinja2.py с этим содержимым:

from __future__ import absolute_import  # Python 2 only

from django.contrib.staticfiles.storage import staticfiles_storage
from django.core.urlresolvers import reverse

from jinja2 import Environment


def environment(**options):
    env = Environment(**options)
    env.globals.update({
        'static': staticfiles_storage.url,
        'url': reverse,
    })
    return env

и установить параметр 'environment' на значение 'myproject.jinja2.environment'.

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

<img src="{{ static('path/to/company-logo.png') }}" alt="Company Logo">

<a href="{{ url('admin:index') }}">Administration</a>

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

Кастомные бэкэнды

Вот как реализовать кастомный бэкэнд шаблонов для использования другой системы шаблонов. Бэкэнд шаблона — это класс, который наследует django.template.backends.base.BaseEngine. Он должен реализовать get_template() и необязательно from_string(). Вот пример для вымышленной библиотеки шаблонов foobar:

from django.template import TemplateDoesNotExist, TemplateSyntaxError
from django.template.backends.base import BaseEngine
from django.template.backends.utils import csrf_input_lazy, csrf_token_lazy

import foobar


class FooBar(BaseEngine):

    # Name of the subdirectory containing the templates for this engine
    # inside an installed application.
    app_dirname = 'foobar'

    def __init__(self, params):
        params = params.copy()
        options = params.pop('OPTIONS').copy()
        super(FooBar, self).__init__(params)

        self.engine = foobar.Engine(**options)

    def from_string(self, template_code):
        try:
          return Template(self.engine.from_string(template_code))
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)

    def get_template(self, template_name):
        try:
            return Template(self.engine.get_template(template_name))
        except foobar.TemplateNotFound as exc:
            raise TemplateDoesNotExist(exc.args, backend=self)
        except foobar.TemplateCompilationFailed as exc:
            raise TemplateSyntaxError(exc.args)


class Template(object):

    def __init__(self, template):
        self.template = template

    def render(self, context=None, request=None):
        if context is None:
            context = {}
        if request is not None:
            context['request'] = request
            context['csrf_input'] = csrf_input_lazy(request)
            context['csrf_token'] = csrf_token_lazy(request)
        return self.template.render(context)

См. DEP 182 для получения дополнительной информации.

Интеграция отладки для кастомных движков

Была добавлена интеграция отладочной страницы для движков шаблонов, не являющихся Django.

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

Postmortem шаблона

Postmortem отображается, когда возникает исключение TemplateDoesNotExist. Он перечисляет движки шаблонов и загрузчики, которые использовались при попытке найти данный шаблон. Например, если настроены два движка Django, postmortem будет выглядеть так:

../_images/postmortem.png

Кастомные движки могут заполнять postmortem, передавая аргументы backend и tried при возникновении исключения TemplateDoesNotExist. Бэкэнды, использующие postmortem, должны указать происхождение в объекте шаблона.

Информация о контекстных строках

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

../_images/template-lines.png

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

  • 'name': Имя шаблона, в котором произошла ошибка.
  • 'message': Сообщение об ошибке.
  • 'source_lines': Строки перед, после и включая строку, на которой произошла ошибка. Это контекст, поэтому он не должен содержать более 20 строк.
  • 'line': Номер строки, на которой произошла ошибка.
  • 'before': Содержимое строки ошибки до токена, вызвавшего ошибку.
  • 'during': Токен, вызвавший ошибку.
  • 'after': Содержимое строки ошибки после токена, вызвавшего ошибку.
  • 'total': Количество строк в source_lines.
  • 'top': Номер строки, где начинается source_lines.
  • 'bottom': Номер строки, где заканчивается source_lines.

Учитывая ошибку шаблона выше, template_debug будет выглядеть так:

{
    'name': '/path/to/template.html',
    'message': "Invalid block tag: 'syntax'",
    'source_lines': [
        (1, 'some\n'),
        (2, 'lines\n'),
        (3, 'before\n'),
        (4, 'Hello {% syntax error %} {{ world }}\n'),
        (5, 'some\n'),
        (6, 'lines\n'),
        (7, 'after\n'),
        (8, ''),
    ],
    'line': 4,
    'before': 'Hello ',
    'during': '{% syntax error %}',
    'after': ' {{ world }}\n',
    'total': 9,
    'bottom': 9,
    'top': 1,
}

API происхождения и интеграция сторонних библиотек

В шаблонах Django доступен объект Origin через атрибут template.origin. Это позволяет отображать отладочную информацию в postmortem шаблона, а также в сторонних библиотеках, таких как Django Debug Toolbar.

Кастомные движки могут предоставлять собственную информацию об template.origin путём создания объекта, определяющего следующие атрибуты:

  • 'name': Полный путь к шаблону.
  • 'template_name': Относительный путь к шаблону, переданный в методы загрузки шаблонов.
  • 'loader_name': Необязательная строка, идентифицирующая функцию или класс, используемый для загрузки шаблона, например django.template.loaders.filesystem.Loader.

Язык шаблонов Django

Синтаксис

О данной секции

Это обзор синтаксиса языка шаблонов Django. Подробности см. в справочнике по синтаксису языка.

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

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

Синтаксис языка шаблонов Django включает четыре конструкции.

Переменные

Переменная выводит значение из контекста, который представляет собой объект типа dict, сопоставляющий ключи со значениями.

Переменные заключены в {{ и }} следующим образом:

My first name is {{ first_name }}. My last name is {{ last_name }}.

При контексте {'first_name': 'John', 'last_name': 'Doe'}, этот шаблон отображается как:

My first name is John. My last name is Doe.

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

{{ my_dict.key }}
{{ my_object.attribute }}
{{ my_list.0 }}

Если переменная разрешается в вызываемую функцию, система шаблонов её вызовет без аргументов и воспользуется результатом, а не самой функцией.

Теги

Теги обеспечивают произвольную логику в процессе рендеринга.

Это определение преднамеренно неопределённо. Например, тег может выводить содержимое, служить конструкцией управления, например, оператором «if» или циклом «for», получать содержимое из базы данных или даже предоставлять доступ к другим тегам шаблонов.

Теги заключены в {% и %} следующим образом:

{% csrf_token %}

Большинство тегов принимают аргументы:

{% cycle 'odd' 'even' %}

Некоторые теги требуют начального и конечного тегов:

{% if user.is_authenticated %}Hello, {{ user.username }}.{% endif %}

Также доступна ссылка на встроенные теги, а также инструкции по написанию кастомных тегов.

Фильтры

Фильтры преобразуют значения переменных и аргументов тегов.

Они выглядят так:

{{ django|title }}

При контексте {'django': 'the web framework for perfectionists with deadlines'}, этот шаблон отображается как:

The Web Framework For Perfectionists With Deadlines

Некоторые фильтры принимают аргументы:

{{ my_date|date:"Y-m-d" }}

Также доступна ссылка на встроенные фильтры, а также инструкции по написанию кастомных фильтров.

Комментарии

Комментарии выглядят так:

{# this won't be rendered #}

Тег {% comment %} предоставляет многострочные комментарии.

Компоненты

О данной секции

Это обзор API языка шаблонов Django. Подробности см. в справочнике API.

Движок

django.template.Engine инкапсулирует экземпляр системы шаблонов Django. Основная причина прямого создания экземпляра Engine заключается в использовании языка шаблонов Django вне проекта Django.

django.template.backends.django.DjangoTemplates — это тонкая оболочка, адаптирующая django.template.Engine к API бэкенда шаблонов Django.

Шаблон

django.template.Template представляет собой скомпилированный шаблон. Шаблоны получаются с помощью Engine.get_template() или Engine.from_string()

Аналогично, django.template.backends.django.Template — это тонкая оболочка, адаптирующая django.template.Template к общему API шаблонов.

Контекст

django.template.Context хранит метаданные в дополнение к данным контекста. Он передаётся в Template.render() для рендеринга шаблона.

django.template.RequestContext — это подкласс Context, который хранит текущий HttpRequest и запускает обработчики контекста шаблонов.

В общем API нет эквивалентного понятия. Данные контекста передаются в обычном dict, а текущий HttpRequest передаётся отдельно при необходимости.

Загрузчики

Загрузчики шаблонов отвечают за поиск шаблонов, их загрузку и возврат объектов Template.

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

Обработчики контекста

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

Их основное использование — добавление общих данных, общих для всех шаблонов, в контекст без повторения кода в каждом представлении.

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

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.9/topics/templates/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API