Django_-_podrobnoe_rukovodstvo
.pdf70 |
Глава 4. Шаблоны |
Поиск имени может быть и многоуровневым. Например, конструкция {{ person.name.upper }} транслируется в последовательность из двух шагов: сначала производится обращение к словарю (person[‘name’]), а затем – вызов метода (upper()):
>>>from django.template import Template, Context
>>>person = {‘name’: ‘Sally’, ‘age’: ‘43’}
>>>t = Template(‘{{ person.name.upper }} is {{ person.age }} years old.’)
>>>c = Context({‘person’: person})
>>>t.render(c)
u’SALLY is 43 years old.’
Вызовы методов
Вызовы методов несколько сложнее, чем другие виды доступа по точке. Перечислим несколько моментов, которые следует иметь в виду.
Если во время вызова метода возникнет исключение, то оно распространяется вверх по стеку, если в объекте-исключении нет атрибута silent_variable_failure со значением True. Если же такой атрибут имеется, то при отображении переменная заменяется пустой строкой, как
вследующем примере:
>>>t = Template(“Меня зовут {{ person.first_name }}.”)
>>>class PersonClass3:
... |
def first_name(self): |
... |
raise AssertionError, “foo” |
>>>p = PersonClass3()
>>>t.render(Context({“person”: p})) Traceback (most recent call last):
...
AssertionError: foo
>>>class SilentAssertionError(AssertionError):
... silent_variable_failure = True
>>>class PersonClass4:
... |
def first_name(self): |
... |
raise SilentAssertionError |
>>>p = PersonClass4()
>>>t.render(Context({“person”: p})) u’Меня зовут .’
•• Вызов метода возможен, только если у метода нет обязательных аргументов. В противном случае система начнет проверять следующий по порядку вид доступа (поиск элемента списка по индексу).
•• Очевидно, что у некоторых методов есть побочные действия, и было бы глупо и, быть может, даже небезопасно разрешать системе шаб лонов обращаться к ним.
Пусть, например, в классе BankAccount имеется метод delete(). Если в шаблоне встречается конструкция {{ account.delete }}, где account –
Использование системы шаблонов |
71 |
объект класса BankAccount, то при отображении такого шаблона объект будет удален!
Чтобы предотвратить такое развитие событий, задайте для метода атрибут alters_data:
def delete(self):
# Удаление счета delete.alters_data = True
•• Система шаблонов не будет вызывать метод, помеченный таким образом. В приведенном выше примере, если в шаблоне встретится конструкция {{ account.delete }} и для метода delete() будет задан атрибут alters_data=True, то метод delete() не будет вызван при отображении. Он просто будет проигнорирован.
Как обрабатываются переменные, отсутствующие в контексте
По умолчанию, если переменная не определена в контексте, в процессе отображения система шаблонов выведет вместо нее пустую строку. Рассмотрим такой пример:
>>>from django.template import Template, Context
>>>t = Template(‘Вас зовут {{ name }}.’)
>>>t.render(Context())
u’Вас зовут .’
>>>t.render(Context({‘var’: ‘hello’})) u’Вас зовут .’
>>>t.render(Context({‘NAME’: ‘hello’})) u’Вас зовут .’
>>>t.render(Context({‘Name’: ‘hello’})) u’Вас зовут .’
Система не возбуждает исключения, поскольку спроектирована так, чтобы прощать ошибки пользователя. В данном случае все виды поиска завершаются неудачно, потому что имя переменной задано неправильно или не в том регистре. В действующих приложениях недопустимо, чтобы сайт оказывался недоступен из-за мелкой синтаксической ошибки в шаблоне.
Модификация контекстных объектов
В большинстве случаев при создании объектов Context конструктору передается заполненный словарь. Но разрешается добавлять и удалять элементы в объект Context и после его создания, для чего применяется стандартный синтаксис Python:
>>>from django.template import Context
>>>c = Context({“foo”: “bar”})
>>>c[‘foo’]
‘bar’
72 |
Глава 4. Шаблоны |
>>>del c[‘foo’]
>>>c[‘foo’]
Traceback (most recent call last):
...
KeyError: ‘foo’
>>>c[‘newvariable’] = ‘hello’
>>>c[‘newvariable’]
‘hello’
Простые шаблонные теги и фильтры
Мы уже отмечали, что в систему шаблонов уже встроен ряд тегов и фильтров. В следующих разделах мы приведем обзор наиболее употребительных.
Теги
В следующих разделах описаны часто используемые теги Django.
if/else
Тег {% if %} вычисляет переменную, и если она равна True (то есть существует, не пуста и не равна булевскому значению False), то система выводит все, что находится между тегами {% if %} и {% endif %}, как в примере ниже:
{% if today_is_weekend %}
<p>Вот и выходной настал!</p> {% endif %}
«Истинность» в языке Python
В языке Python и в системе шаблонов Django следующие объекты принимают значение False при вычислении в булевском контексте:
•• Пустой список ([])
•• Пустой кортеж (())
•• Пустой словарь ({})
•• Пустая строка (‘’)
•• Нуль (0)
•• Специальный объект None
•• Объект False (по очевидным причинам)
•• Пользовательские объекты, для которых определено поведение в булевском контексте. (Эта тема не для начинающих.)
Все остальное принимает значение True.
Простые шаблонные теги и фильтры |
73 |
Тег {% else %} необязателен:
{% if today_is_weekend %}
<p>Вот и выходной настал!</p> {% else %}
<p>Пора снова на работу.</p> {% endif %}
Тег {% if %} допускает использование операторов and, or и not для проверки нескольких переменных и вычисления логического отрицания, например:
{% if athlete_list and coach_list %} Есть и спортсмены, и тренеры.
{% endif %}
{% if not athlete_list %} Спортсменов нет.
{% endif %}
{% if athlete_list or coach_list %} Есть спортсмены или тренеры.
{% endif %}
{% if not athlete_list or coach_list %} Нет спортсменов или есть тренеры.
{% endif %}
{% if athlete_list and not coach_list %} Есть спортсмены и ни одного тренера.
{% endif %}
Нельзя использовать операторы and и or в одном и том же теге {% if %}, поскольку порядок их вычисления неоднозначен. Например, следующий тег недопустим:
{% if athlete_list and coach_list or cheerleader_list %}
Использование скобок для управления порядком выполнения операций не поддерживается. Если возникает нужда в скобках, подумайте о том, чтобы вынести логику за пределы шаблона, а в шаблон передать результат вычислений. Или просто воспользуйтесь вложенными тегами {% if %}, как в следующем примере:
{% if athlete_list %}
{% if coach_list or cheerleader_list %}
Есть спортсмены и тренеры или группа поддержки! {% endif %}
{% endif %}
Многократное употребление одного и того же логического оператора допускается, но комбинировать разные операторы нельзя. Например, такая конструкция допустима:
{% if athlete_list or coach_list or parent_list or teacher_list %}
74 |
Глава 4. Шаблоны |
Тега {% elif %} не существует. Для достижения эквивалентного результата используйте вложенные теги {% if %}:
{% if athlete_list %}
<p>Список спортсменов: {{ athlete_list }}.</p> {% else %}
<p>Нет никаких спортсменов.</p> {% if coach_list %}
<p>Список тренеров: {{ coach_list }}.</p> {% endif %}
{% endif %}
Не забывайте закрывать каждый тег {% if %} соответствующим тегом {% endif %}. В противном случае Django возбудит исключение
TemplateSyntaxError.
for
Тег {% for %} позволяет выполнить обход всех элементов последовательности. Синтаксис аналогичен инструкции for в языке Python: for X in Y, где Y – последовательность, а X – имя переменной, указывающей на текущий элемент на очередной итерации. На каждой итерации система шаблонов выводит все между тегами {% for %} и {% endfor %}.
Например, следующим образом можно вывести список спортсменов, содержащийся в переменной athlete_list:
<ul>
{% for athlete in athlete_list %} <li>{{ athlete.name }}</li>
{% endfor %} </ul>
Для обхода списка в обратном порядке пользуйтесь оператором reversed:
{% for athlete in athlete_list reversed %}
...
{% endfor %}
Теги {% for %} могут быть вложенными:
{% for athlete in athlete_list %} <h1>{{ athlete.name }}</h1> <ul>
{% for sport in athlete.sports_played %} <li>{{ sport }}</li>
{% endfor %} </ul>
{% endfor %}
Обычно перед началом цикла принято проверять размер списка и, если список пуст, выводить какой-нибудь подходящий текст:
Простые шаблонные теги и фильтры |
75 |
{% if athlete_list %}
{% for athlete in athlete_list %} <p>{{ athlete.name }}</p>
{% endfor %} {% else %}
<p>Спортсменов нет. Только программисты.</p> {% endif %}
Поскольку такая ситуация встречается очень часто, тег for поддерживает необязательную часть {% empty %}, с помощью которой можно определить, что делать, когда список пуст. Следующий пример эквивалентен предыдущему:
{% for athlete in athlete_list %} <p>{{ athlete.name }}</p>
{% empty %}
<p>Спортсменов нет. Только программисты.</p> {% endfor %}
Не существует способа выйти из цикла до его естественного завершения. Если нечто подобное необходимо, измените переменную последовательности, обход которой осуществляется в цикле, так чтобы она содержала только нужные значения. Точно так же не существует аналога инструкции «continue», который позволял бы немедленно перейти в начало цикла. (Ниже, в разделе «Идеология и ограничения», объясняется, почему принято такое проектное решение.)
Внутри цикла {% for %} имеется доступ к шаблонной переменной с именем forloop. У нее есть несколько атрибутов, позволяющих получить сведения о текущем состоянии цикла:
•• forloop.counter всегда показывает, сколько итераций цикла уже выполнено. Отсчет начинается с единицы, поэтому на первой итерации forloop.counter равен 1. Пример:
{% for item in todo_list %}
<p>{{ forloop.counter }}: {{ item }}</p> {% endfor %}
•• forloop.counter0 аналогичен forloop.counter с тем отличием, что отсчет начинается с нуля. На первой итерации цикла значение этого атрибута равно 0.
•• forloop.revcounter всегда показывает, сколько итераций осталось выполнить. На первой итерации цикла значение атрибута forloop. revcounter равно количеству элементов в перебираемом списке. На последней итерации этот счетчик равен 1.
•• forloop.revcounter0 аналогичен forloop.revcounter с тем отличием, что на первой итерации значение этого атрибута равно количеству элементов в перебираемом списке минус 1, а на последней равно 0.
76 |
Глава 4. Шаблоны |
•• forloop.first – булевское значение, равное True на первой итерации цикла. Этот атрибут удобен для обработки граничных случаев:
{% for object in objects %}
{% if forloop.first %}<li class=”first”>{% else %}<li>{% endif %} {{ object }}
</li> {% endfor %}
•• forloop.last – булевское значение, равное True на последней итерации цикла. Часто применяется для вставки знаков разделителей между элементами списка ссылок:
{% for link in links %} {{ link }}
{% if not forloop.last %}
|
{% endif %}{% endfor %}
Этот код порождает примерно такой результат:
Link1 | Link2 | Link3 | Link4
Еще одно типичное применение – вставка запятых между словами в списке:
Любимые места:
{% for p in places %} {{ p }}
{% if not forloop.last %}
,
{% endif %} {% endfor %}
•• forloop.parentloop – ссылка на объект forloop объемлющего цикла в случае, когда циклы вложены, например:
{% for country in countries %} <table>
{% for city in country.city_list %} <tr>
<td>Страна #{{ forloop.parentloop.counter }}</td> <td>Город #{{ forloop.counter }}</td>
<td>{{ city }}</td> </tr>
{% endfor %} </table>
{% endfor %}
Магическая переменная forloop доступна только внутри циклов. Когда интерпретатор шаблона доходит до тега {% endfor %}, объект forloop исчезает.
Простые шаблонные теги и фильтры |
77 |
Контекст и переменная forloop
Внутри блока {% for %} существующие переменные маскируются, чтобы избежать перезаписи магической переменной forloop. Django предоставляет доступ к замаскированному контексту через объект forloop.parentloop. Обычно это не вызывает никаких осложнений, но если в шаблон передана переменная с именем forloop (а мы настоятельно рекомендуем этого не делать, чтобы не вводить в заблуждение своих коллег), то внутри блока {% for %} она будет называться forloop.parentloop.
ifequal/ifnotequal
Систему шаблонов в Django сознательно не стали превращать в полноценный язык программирования, поэтому выполнять произвольные предложения языка Python она не может. (Дополнительные сведения по этому поводу см. в разделе «Идеология и ограничения».) Однако в шаблонах часто возникает необходимость сравнить два значения и вывести что-то, если они равны. Для этой цели Django предлагает тег
{% ifequal %}.
Тег {% ifequal %} сравнивает два значения и, если они равны, выводит все, что находится между тегами {% ifequal %} и {% endifequal %}.
Вследующем примере сравниваются шаблонные переменные user
иcurrentuser:
{% ifequal user currentuser %} <h1>Добро пожаловать!</h1>
{% endifequal %}
В качестве аргументов могут выступать строковые литералы, заключенные в одиночные или двойные кавычки, поэтому следующий шаб лон допустим:
{% ifequal section ‘sitenews’ %} <h1>Новости сайта</h1>
{% endifequal %}
{% ifequal section “community” %} <h1>Сообщество</h1>
{% endifequal %}
Так же как и {% if %}, тег {% ifequal %} поддерживает необязательную ветвь {% else %}:
{% ifequal section ‘sitenews’ %} <h1>Новости сайта</h1>
{% else %}
78 |
Глава 4. Шаблоны |
<h1>Новостей нет</h1> {% endifequal %}
Аргументами тега {% ifequal %} могут быть только шаблонные переменные, строки, целые числа и числа с плавающей точкой. Ниже приведены примеры допустимого кода:
{% ifequal variable 1 %}
{% ifequal variable 1.23 %} {% ifequal variable ‘foo’ %} {% ifequal variable “foo” %}
Все прочие типы переменных, например, словари, списки, булевские значения, употреблять в качестве литералов в теге {% ifequal %} не разрешается. Ниже приведены примеры недопустимого кода:
{% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {‘key’: ‘value’} %}
Если необходимо проверить, истинна или ложна некая переменная, пользуйтесь тегом {% if %} вместо {% ifequal %}.
Комментарии
Как и в HTML или Python, в языке шаблонов Django могут быть комментарии. Комментарий обозначается тегом {# #}:
{# Это комментарий #}
При отображении шаблона комментарий не выводится.
Показанный выше синтаксис не допускает многострочных комментариев. Это ограничение повышает скорость синтаксического анализа шаблона. В следующем примере выведенный текст будет выглядеть в точности так же, как сам шаблон (то есть тег комментария не воспринимается как комментарий):
Это {# это не комментарий #} тест.
Если вам понадобятся многострочные комментарии, используйте шаб лонный тег {% comment %}:
{% comment %} Это
многострочный комментарий. {% endcomment %}
Фильтры
Как было сказано выше, шаблонные фильтры дают простой способ изменить внешний вид переменной при отображении. В фильтрах используется символ конвейера, например:
Идеология и ограничения |
79 |
{{name|lower }}
Врезультате выводится значение переменной {{ name }}, пропущенное через фильтр lower, который преобразует текст в нижний регистр.
Фильтры можно объединять в цепочки, то есть подавать выход одного фильтра на вход следующего. В примере ниже первый элемент списка преобразуется в верхний регистр:
{{ my_list|first|upper }}
Некоторые фильтры принимают аргументы. Аргумент фильтра указывается после двоеточия и заключается в двойные кавычки, например:
{{bio|truncatewords:”30” }}
Врезультате выводятся первые 30 слов из значения переменной bio.
Ниже описываются наиболее употребительные фильтры. Остальные описаны в приложении F.
•• addslashes: добавляет символ обратного слеша перед каждым из следующих символов: обратный слеш, одиночная кавычка, двойная кавычка. Это полезно, когда генерируемый текст предполагается включить в JavaScript-сценарий.
•• date: форматирует объект date или datetime в соответствии со строкой формата, переданной в качестве параметра, например:
{{ pub_date|date:”F j, Y” }}
Форматные строки описаны в приложении F.
•• length: возвращает длину значения. Для списка возвращает количество элементов в нем, для строки – количество символов. (Для хорошо знающих язык Python отметим, что этот фильтр применим к любому объекту, который умеет вычислять собственную длину, то есть к объектам, в которых определен метод __len__()).
Идеология и ограничения
Теперь, когда вы получили представление о языке шаблонов в Django, мы хотели бы рассказать о некоторых намеренных ограничениях и об идеологических принципах, положенных в основу его работы.
Синтаксис шаблонов субъективен, пожалуй, в большей степени, чем любой другой компонент веб-приложений, и мнения программистов по этому поводу сильно разнятся. Об этом свидетельствует наличие десятков, если не сотен, открытых реализаций языков шаблонов только на Python. И надо полагать, что каждая из них была написана потому, что все существующие языки шаблонов автору не понравились. (Считается даже, что написание собственного языка шаблонов – обряд посвящения в программисты на Python! Если вы еще этого не делали, попробуйте, это хорошее упражнение.)