Руководство часть 6: отображение списков и детальной информации

Автоматическое экранирование HTML¶

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

Hello, {{ name }}

На первый взгляд это кажется безобидным способом отображения имени пользователя, но давайте посмотрим, что произойдет, если пользователь выбрал такое имя::

<script>alert('hello')</script>

С таким именем шаблон вернет:

Hello, <script>alert('hello')</script>

…что приведет к отображению alert-окна JavaScript!

Аналогично, что если имя содержит символ '<'?

<b>username

Шаблон вернет такое содержимое:

Hello, <b>username

…в результате оставшееся содержимое страницы будет выделено полужирным!

Очевидно, пользовательским данными нельзя слепо доверять и вставлять непосредственно в содержимое страницы, так как злоумышленники могут использовать это с плохими намерениями. Такой тип уязвимости называется Cross Site Scripting (XSS) атакой.

Чтобы избежать этой проблемы, у вас есть два варианта:

  • Первый, вы можете применять ко всем сомнительным переменным фильтр (описанный далее), который преобразует потенциально опасные HTML символы в безопасные. Такое решение было принятым в первых версиях Django, но проблема в том, что оно возлагает бремя ответственности за безопасность на вас, разработчика / автора шаблона. Легко забыть экранировать переменную.

  • Второй, вы можете позволить Django автоматически экранировать HTML. Оставшаяся часть этого раздела описывает, как автоматическое экранирование работает.

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

  • < заменяется на &lt;

  • > заменяется на &gt;

  • ' (одинарная кавычка) заменяется на &#39;

  • " (двойная кавычка) заменяется на &quot;

  • & заменяется на &amp;

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

Как это отключить

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

Для отдельных переменных

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

This will be escaped: {{ data }}
This will not be escaped: {{ data|safe }}

Думайте о как сокращение “обезопасить от последующего экранирования” или “может быть смело интерпретировано как HTML”. В этом примере, если data содержит '<b>', будет выведено:

This will be escaped: &lt;b&gt;
This will not be escaped: <b>

Для блоков шаблона

Для контроля авто-экранирования в шаблоне, “оберните” шаблон (или часть шаблона) тегом , например:

{% autoescape off %}
    Hello {{ name }}
{% endautoescape %}

Тег в качестве аргумента принимает on или off. В некоторых случаях, вы захотите включить экранирование в шаблоне, в котором оно было отключено. Например:

Auto-escaping is on by default. Hello {{ name }}

{% autoescape off %}
    This will not be auto-escaped: {{ data }}.

    Nor this: {{ other_data }}
    {% autoescape on %}
        Auto-escaping applies again: {{ name }}
    {% endautoescape %}
{% endautoescape %}

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

base.html

{% autoescape off %}
<h1>{% block title %}{% endblock %}</h1>
{% block content %}
{% endblock %}
{% endautoescape %}

child.html

{% extends "base.html" %}
{% block title %}This &amp; that{% endblock %}
{% block content %}{{ greeting }}{% endblock %}

Так как авто-экранирование отключено в базовом шаблоне, оно будет отключено и в дочернем шаблоне. Если переменная greeting равна <b>Hello!</b>, будет выведено:

<h1>This &amp; that</h1>
<b>Hello!</b>

Заметки

Обычно, авторы шаблонов не должны волноваться про авто-экранирование. Разработчики на стороне Python (те, кто создает представления и фильтры) должны определять в каких случаях данные не должны экранироваться и помечать их соответствующим образом, так что все будет просто работать в шаблонах.

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

Теги¶

Теги выглядят таким образом: {% tag %}. Теги сложнее чем переменные: одни создают текст для вывода, влияют на выполнение используя условия и циклы, другие загружают дополнительную информацию в шаблоны, чтобы использовать ее далее через переменные.

Некоторые теги требуют открывающий и закрывающий теги (например, {% tag %} ... содержимое тега ... {% endtag %}).

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

Цикл по каждому элементу массива. Например, выведем список спортсменов из athlete_list:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
, elif, и else

Вычисляет переменную и если она равна “true”, выводит содержимое блока:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
    Athletes should be out of the locker room soon!
{% else %}
    No athletes.
{% endif %}

В примере выше, если athlete_list не пустой, будет отображено количество спортсменов {{ athlete_list|length }}. Иначе, если athlete_in_locker_room_list не пустой, будет показано сообщение “Athletes should be out…”. Если оба списка пустые, будет показано сообщение “No athletes.”.

Вы можете использовать фильтры и операторы в теге tag:

{% if athlete_list|length > 1 %}
   Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
   Athlete: {{ athlete_list.0.name }}
{% endif %}

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

и

Определяет (смотрите ниже), эффективный способ использовать шаблоны.

Опять же, это только несколько тегов; полный список смотрите в .

Вы можете создать собственный тег; смотрите Собственные шаблонные теги и фильтры.

Обзор

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

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

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

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

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

Let’s look at template inheritance by starting with an example:

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css">
    <title>{% block title %}My amazing site{% endblock %}</title>
</head>

<body>
    <div id="sidebar">
        {% block sidebar %}
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
        {% endblock %}
    </div>

    <div id="content">
        {% block content %}{% endblock %}
    </div>
</body>
</html>

This template, which we’ll call , defines an HTML skeleton
document that you might use for a two-column page. It’s the job of «child»
templates to fill the empty blocks with content.

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

Дочерний шаблон может выглядеть таким образом:

{% extends "base.html" %}

{% block title %}My amazing blog{% endblock %}

{% block content %}
{% for entry in blog_entries %}
    <h2>{{ entry.title }}</h2>
    <p>{{ entry.body }}</p>
{% endfor %}
{% endblock %}

Ключевым здесь есть тег . Он говорит механизму шаблонов, что этот шаблон «наследует» другой шаблон. Когда механизм шаблонов выполняет этот шаблон, первым делом находится родительский шаблон – в этом примере «base.html».

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

<!DOCTYPE html>
<html lang="en">
<head>
    <link rel="stylesheet" href="style.css">
    <title>My amazing blog</title>
</head>

<body>
    <div id="sidebar">
        <ul>
            <li><a href="/">Home</a></li>
            <li><a href="/blog/">Blog</a></li>
        </ul>
    </div>

    <div id="content">
        <h2>Entry one</h2>
        <p>This is my first entry.</p>

        <h2>Entry two</h2>
        <p>This is my second entry.</p>
    </div>
</body>
</html>

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

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

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

This approach maximizes code reuse and helps to add items to shared content
areas, such as section-wide navigation.

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

  • Если вы используете , он должен быть первым тегом в шаблоне. Иначе наследование не будет работать.

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

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

  • Если вам необходимо содержимое блока родительского шаблона, используйте переменную . Эта полезно, если вам необходимо дополнить содержимое родительского блока, а не полностью переопределить его. Содержимое не будет автоматически экранировано (смотрите ), так как оно уже было экранировано, при необходимости, в родительском шаблоне.

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

    {% trans "Title" as title %}
    {% block content %}{{ title }}{% endblock %}
    
  • Для ясности, вы можете добавить название вашему тегу . Например:

    {% block content %}
    ...
    {% endblock content %}
    

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

Документация Django библиотек

Рецепты Django ORM

Рецепты Django ORM — это книга о работе с моделями Django ORM и Django. Django ORM является одним из ключевых столпов Django. Он предоставляет абстракции …

Django Rest Framework

Django Rest Framework (DRF) — это библиотека, которая работает со стандартными моделями Django для создания гибкого и мощного API для проекта.

Django CMS

Django CMS — это современная платформа для веб-публикаций, построенная на Django, фреймворке веб-приложений «для перфекционистов с соблюдением сроков». Django CMS предлагает готовую поддержку общих функций, …

Channels

Channels — это проект, который использует Django и расширяет его возможности за пределы HTTP — для обработки WebSockets, протоколов чата, IoT-протоколов и многого другого. Он …

ASGI — спецификация и утилиты

ASGI (Asynchronous Server Gateway Interface) является духовным наследником WSGI, предназначенным для обеспечения стандартного интерфейса между асинхронными веб-серверами, платформами и приложениями Python. WSGI предоставил стандарт для …

Python Social Auth

Python Social Auth — это простой в настройке механизм социальной аутентификации/регистрации с поддержкой нескольких платформ и провайдеров аутентификации. Созданный с использованием базового кода из django-social-auth, …

Собственные библиотеки тегов и фильтров¶

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

{% load humanize %}

{{ 45000|intcomma }}

В это примере, тег загружает библиотеку , которая предоставляет тег . Если вы активировали , в разделе документации вашего интерфейса администратора вы можете найти список всех установленных библиотек.

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

{% load humanize i18n %}

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

Собственные библиотеки и наследование шаблонов

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

Например, если шаблон содержит , дочерний шаблон (например, содержащий«{% extends «foo.html» %}«) не сможет использовать теги и фильтры из этой библиотеки. Дочерний шаблон должен самостоятельно загрузить библиотеку, используя .

Так сделано ради «maintainability» и «sanity».

См.также

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

Теги¶

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

Некоторые теги требуют открывающий и закрывающий теги (например, ).

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

Цикл по каждому элементу массива. Например, выведем список спортсменов из :

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
, , and

Вычисляет переменную и если она равна «true», выводит содержимое блока:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
    Athletes should be out of the locker room soon!
{% else %}
    No athletes.
{% endif %}

В примере выше, если не пустой, будет отображено количество спортсменов . Иначе, если не пустой, будет показано сообщение «Athletes should be out…». Если оба списка пустые, будет показано сообщение «No athletes.».

Вы можете использовать фильтры и операторы в теге tag:

{% if athlete_list|length > 1 %}
   Team {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
   Athlete {{ athlete_list.name }}
{% endif %}

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

and
Определяет (смотрите ниже), эффективный способ использовать шаблоны.

Опять же, это только несколько тегов; полный список смотрите в .

Вы можете создать собственный тег; смотрите Собственные шаблонные теги и фильтры.

Теги¶

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

Некоторые теги требуют открывающий и закрывающий теги (например, ).

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

Цикл по каждому элементу массива. Например, выведем список спортсменов из :

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>
, , и

Вычисляет переменную и если она равна “true”, выводит содержимое блока:

{% if athlete_list %}
    Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
    Athletes should be out of the locker room soon!
{% else %}
    No athletes.
{% endif %}

В примере выше, если не пустой, будет отображено количество спортсменов . Иначе, если не пустой, будет показано сообщение “Athletes should be out…”. Если оба списка пустые, будет показано сообщение “No athletes.”.

Вы можете использовать фильтры и операторы в теге tag:

{% if athlete_list|length > 1 %}
   Team: {% for athlete in athlete_list %} ... {% endfor %}
{% else %}
   Athlete: {{ athlete_list.0.name }}
{% endif %}

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

и

Определяет (смотрите ниже), эффективный способ использовать шаблоны.

Опять же, это только несколько тегов; полный список смотрите в .

Вы можете создать собственный тег; смотрите Собственные шаблонные теги и фильтры.

Вызов методов¶

Вызов большинства методов объектов также доступен в шаблоне. Это означает, что шаблон имеет доступ не только к атрибутам классов (например, название поля) и переменных переданных из представлениях. Например, Django ORM предоставляет атрибут для получения связанных через внешний ключ объектов. Следовательно, для модели комментариев(“comment”) с внешним ключом на модель задач(“task”) вы можете обратиться в цикле ко всем связанным комментариям переданного объекта задачи:

{% for comment in task.comment_set.all %}
    {{ comment }}
{% endfor %}

Также, QuerySets предоставляет метод count() для получения количества объектов. Следовательно, вы можете получить количество комментариев связанных с конкретной задачей:

{{ task.comment_set.all.count }}

И конечно вы можете использовать методы вашей модели:

models.py

class Task(models.Model):
    def foo(self):
        return "bar"

template.html

{{ task.foo }}

Переменные¶

Variables look like this: . When the template engine
encounters a variable, it evaluates that variable and replaces it with the
result. Variable names consist of any combination of alphanumeric characters
and the underscore () but may not start with an underscore, and may not
be a number. The dot () also appears in variable sections, although that
has a special meaning, as indicated below. Importantly, you cannot have spaces
or punctuation characters in variable names.

Используйте точку () Для доступа к атрибутам переменной.

За кулисами

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

  • Поиск в словаре
  • Поиск атрибута или метода
  • Поиск по числовому индексу

Если полученное значение вызывается, оно вызывается без аргументов. Результат вызова становится значением шаблона.

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

{% for k, v in defaultdict.items %}
    Do something with k and v here...
{% endfor %}

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

В приведенном выше примере будет заменен атрибутом объекта .

Если вы используете переменную, которой не существует, система шаблонов вставит значение параметра , который по умолчанию установлен на (пустая строка).

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

Доступ к вызовам методов¶

Большинство вызовов методов, прикрепленных к объектам, также доступны из шаблонов. Это означает, что шаблоны имеют доступ к гораздо большему, чем просто атрибуты класса (например, имена полей) и переменные, передаваемые из представлений. Например, Django ORM предоставляет синтаксис для поиска коллекции объектов, связанных с внешним ключом. Следовательно, учитывая модель под названием «comment» с отношением внешнего ключа к модели под названием «task», вы можете просмотреть все комментарии, прикрепленные к данной задаче, следующим образом:

{% for comment in task.comment_set.all %}
    {{ comment }}
{% endfor %}

Аналогичным образом QuerySets предоставляет метод для подсчета количества содержащихся в них объектов. Следовательно, вы можете получить количество всех комментариев, относящихся к текущей задаче, с помощью:

{{ task.comment_set.all.count }}

Вы также можете получить доступ к методам, которые вы явно определили в своих собственных моделях:

models.py

class Task(models.Model):
    def foo(self):
        return "bar"

template.html

{{ task.foo }}

Языковая модель — вызов

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

Некоторые вещи, которые следует учитывать:

  • Должен ли «язык» ассоциироваться с Book, BookInstance или каким-либо другим объектом?
  • Должны ли быть представлены разные языки с использованием модели, свободного текстового поля или жёстко запрограммированного списка выбора?

После того, как вы решили, добавьте поле. Вы можете увидеть наше решение на Github here.

Постраничный вывод (Pagination)

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

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

Откройте catalog/views.py и добавьте поле как показано жирным в следующем фрагменте.

Как только у вас появится более 10 записей в базе данных отображение начнёт формировать постраничный вывод данных, которые он передаёт шаблону. К различным страницам данного вывода можно получить доступ при помощи параметров GET-запроса — к странице 2 вы можете получить доступ, используя URL-адрес: .

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

Откройте /locallibrary/catalog/templates/base_generic.html и, ниже блока , вставьте блок (во фрагменте не выделен жирным), отвечающий за постраничный вывод. Данный код, в первую очередь, проверяет «включён» ли механизм постраничного вывода для данной страницы и если это так, то он добавляет ссылки и соответственно (а также, номер текущей страницы). 

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

Мы используем для получения URL-адреса текущей страницы, для того, чтобы создать ссылки на соответствующие страницы, обратите внимание, что данный вызов не зависит от объекта и, таким образом, может использоваться отдельно. На этом все!

На этом все!

Картинка ниже показывает как выглядит постраничный вывод — если вы не добавили более 10 записей в вашу базу данных, тогда вы можете проверить как это работает, просто уменьшив значение в в файле catalog/views.py. Для получения результата, соответствующего картинке ниже, мы изменили.

Ссылки на страницы показаны в нижней части страницы. Показаны ссылки следующая/предыдущая в зависимости от того на какой странице вы в данный момент находитесь.

Рейтинг
( Пока оценок нет )
Понравилась статья? Поделиться с друзьями:
Мой редактор ОС
Добавить комментарий

;-) :| :x :twisted: :smile: :shock: :sad: :roll: :razz: :oops: :o :mrgreen: :lol: :idea: :grin: :evil: :cry: :cool: :arrow: :???: :?: :!: