Шаблоны Django

Шаблонная система Django (Template System) — это ключевой компонент, используемый для отделения бизнес-логики (Python) от уровня представления (HTML). Она позволяет разработчикам динамически генерировать HTML-страницы с помощью простых тегов и переменных.

В предыдущей главе мы использовалиdjango.http.HttpResponse()для вывода"Hello World!"Такой подход смешивает данные и представления вместе, что не соответствует философии MVT Django.

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

ОсобенностиСинтаксис/ПримерыПрименимые сценарии
Рендеринг переменных{{ variable }}Динамическое отображение данных
Управление логикой{% if %}, {% for %}Условный/циклический рендеринг
Наследование шаблонов{% extends %}, {% block %}Избегание дублирования HTML-структур
Статические файлы{% static 'path' %}Загрузка CSS/JS/изображений
Пользовательские фильтры@register.filterРасширение функциональности шаблонов

Примеры применения шаблонов

Продолжая проект из предыдущей главы, мы создадим каталог templates в каталоге HelloWorld и создадим файл example.html. Вся структура каталогов выглядит следующим образом:

HelloWorld/
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- settings.py
|   |-- settings.pyc
|   |-- urls.py
|   |-- urls.pyc
|   |-- views.py
|   |-- views.pyc
|   |-- wsgi.py
|   `-- wsgi.pyc
|-- manage.py
`-- templates
    `-- example.html

Код файла example.html выглядит следующим образом:

Код файла HelloWorld/templates/example.html:

<h1>{{ hello }}</h1>

Из шаблонов мы знаем, что переменные используют двойные фигурные скобки.{{ }}。

Далее нам нужно сообщить Django путь к файлу шаблона. Откройте файл HelloWorld/HelloWorld/settings.py и изменитеTEMPLATESвDIRSна[BASE_DIR / "templates"], как показано ниже:

Код файла HelloWorld/HelloWorld/settings.py:

...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR / "templates"],       # Изменить здесь
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
...

Теперь мы изменяем views.py, чтобы добавить новый объект для передачи данных в шаблон:

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render def example(request): context = {} context['hello'] = 'Привет, мир!' return render(request, 'example.html', context)

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

Пары ключ-значение элементов в словаре context.helloСоответствует переменным в шаблоне{{ hello }}。

Код файла HelloWorld/HelloWorld/urls.py:

from django.contrib import admin from django.urls import path from . import views urlpatterns = [ path('admin/', admin.site.urls), path('example/', views.example), ]

Затем перейдите в каталог HelloWorld и выполните следующую команду, чтобы запустить сервер:

python3 manage.py runserver 0.0.0.0:8000

Посетите сноваhttp://127.0.0.1:8000/example, вы увидите страницу:

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

Далее мы подробно рассмотрим часто используемые правила синтаксиса в шаблонах.


Теги шаблонов Django

Синтаксис:

{% tag %}

Управление логикой шаблона, общие теги:

ТегиНазначение
{% for %}Перебор списков/словарей
{% if %}Условные операторы
{% extends %}Наследование базового шаблона
{% block %}Определение блоков содержимого, которые могут быть переопределены дочерними шаблонами
{% include %}Встраивание других фрагментов шаблона
{% url %}Обратное разрешение URL (чтобы избежать жестко заданных путей)
{% csrf_token %}Генерация CSRF-токена (для POST-форм)

Переменная

Синтаксис шаблона:

view:{"HTML变量名" : "views变量名"}
HTML:{{变量名}}

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
  views_name = "Example"
  return  render(request,"example.html", {"name":views_name})

example.html в папке templates:

<p>{{ name }}</p>

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

Список

В файле example.html в папке templates вы можете использовать.чтобы получить соответствующий элемент по индексу.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    views_list = ["Учебник для новичков 1","Учебник для новичков 2","Учебник для новичков 3"]
    return render(request, "example.html", {"views_list": views_list})

Код файла HelloWorld/templates/example.html:

<p>{{ views_list }}</p># Получить весь список<p>{{ views_list.0 }}</p># Взять первый элемент списка

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

Словарь

В файле example.html в папке templates вы можете использовать.keyчтобы получить соответствующее значение.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    views_dict = {"name":"Пример"}
    return render(request, "example.html", {"views_dict": views_dict})

Код файла HelloWorld/templates/example.html:

<p>{{ views_dict }}</p> <p>{{ views_dict.name }}</p>

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

Filter

Синтаксис шаблона:
{{ Variable name | Filter: optional parameter }}

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

{{ name|lower }}

После обработки переменной {{ name }} фильтром lower текст преобразуется из верхнего регистра в нижний.

Каналы фильтров можно *объединять в цепочку*, то есть вывод одного канала фильтра может служить входом для следующего канала:

{{ my_list|first|upper }}

В приведённом выше примере берётся первый элемент и преобразуется в верхний регистр.

Некоторые фильтры имеют параметры. Параметры фильтра указываются после двоеточия и всегда заключаются в двойные кавычки. Например:

{{ bio|truncatewords:"30" }}

Это отобразит первые 30 слов переменной bio.

Другие фильтры:

  • addslashes: Добавляет обратные слеши перед любыми обратными слешами, одинарными кавычками или двойными кавычками.
  • date : Форматирует объект date или datetime в соответствии с указанным параметром строки формата. Пример:
    {{ pub_date|date:"F j, Y" }}
  • length: возвращает длину переменной.

default

default предоставляет значение по умолчанию для переменной.

Если логическое значение переменной, переданной представлениями, равно false, используется указанное значение по умолчанию.

Следующие значения являются ложными:

0  0.0  False  0j  ""  []  ()  set()  {}  None

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    name =0
    return render(request, "example.html", {"name": name})

Код файла HelloWorld/templates/example.html:

{{ name|default:"Rookie Tutorial 666" }}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

length

Возвращает длину объекта; применимо к строкам и спискам.

Для словаря возвращает количество пар ключ-значение; для множества возвращает длину после удаления дубликатов.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    name ="Example"
    return render(request, "example.html", {"name": name})

Код файла HelloWorld/templates/example.html:

{{ name|length}}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

filesizeformat

Отображает размер файла в более удобочитаемом формате (например, '13 KB', '4.1 MB', '102 bytes' и т. д.).

Для словаря возвращает количество пар ключ-значение; для множества возвращает длину после удаления дубликатов.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    num=1024
    return render(request, "example.html", {"num": num})

Код файла HelloWorld/templates/example.html:

{{ num|filesizeformat}}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

date

Форматирует переменную даты в соответствии с заданным форматом.

ФорматированиеY-m-d H:i:sreturnYear-Month-Day Hour:Minute:Secondвремя в формате.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    import datetime
    now  =datetime.datetime.now()
    return render(request, "example.html", {"time": now})

Код файла HelloWorld/templates/example.html:

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

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

truncatechars

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

Усечённая строка будет заканчиваться...окончанием.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    views_str = "Example"
    return render(request, "example.html", {"views_str": views_str})

Код файла HelloWorld/templates/example.html:

{{ views_str|truncatechars:2}}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

safe

Помечает строку как безопасную, и она не требует экранирования.

Используйте safe только тогда, когда можете гарантировать, что данные, переданные из views.py, абсолютно безопасны.

Он имеет тот же эффект, что и mark_safe в серверном views.py.

Django автоматически экранирует синтаксис тегов, переданный из views.py в HTML-файлы, из-за чего его семантика не срабатывает. Добавление фильтра safe сообщает Django, что данные безопасны и не нуждаются в экранировании, позволяя семантике данных вступить в силу.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    views_str = "<a href='/index.html'>Нажмите, чтобы перейти</a>"
    return render(request, "example.html", {"views_str": views_str})

Код файла HelloWorld/templates/example.html:

{{ views_str|safe }}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

тег if/else

Базовый формат синтаксиса выглядит следующим образом:

{% if condition %}
     ... display
{% endif %}

Или:

{% if condition1 %}
   ... display 1
{% elif condition2 %}
   ... display 2
{% else %}
   ... display 3
{% endif %}

Определяет, выводить ли данные в зависимости от условий. if/else поддерживает вложенность.

Тег {% if %} принимает ключевые слова and, or, not для вычисления нескольких переменных или для отрицания переменной (not), например:

{% if athlete_list and coach_list %}
     athletes 和 coaches 变量都是可用的。
{% endif %}

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    views_num = 88
    return render(request, "example.html", {"num": views_num})

Код файла HelloWorld/templates/example.html:

{%if num > 90 and num <= 100%} Отлично {%elif num > 60 and num <= 90%} Квалифицирован {%else%} Иди играй~ {%endif %}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

тег 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>

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    views_list = ["Пример","Учебник для новичков 1","Учебник для новичков 2","Учебник для новичков 3",]
    return render(request, "example.html", {"views_list": views_list})

Код файла HelloWorld/templates/example.html:

{% for i in views_list %} {{ i }} {% endfor %}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

Добавьте reversed к тегу, чтобы список перебирался в обратном порядке:

{% for athlete in athlete_list reversed %}
...
{% endfor %}

Код файла HelloWorld/templates/example.html:

{% for i in views_list reversed%} {{ i }} {% endfor %}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

Итерация по словарю: Вы можете напрямую использовать словарь.itemsМетод: используйте распаковку переменных, чтобы получить ключи и значения отдельно.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    views_dict = {"name":"Пример","age":18}
    return render(request, "example.html", {"views_dict": views_dict})

Код файла HelloWorld/templates/example.html:

{% for i,j in views_dict.items %} {{ i }}---{{ j }} {% endfor %}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

В теге {% for %} вы можете получить индекс цикла через переменную {{forloop}}.
  • forloop.counter: Возвращает индекс цикла по порядку, начиная с 1.
  • forloop.counter0: Возвращает индекс цикла по порядку, начиная с 0.
  • forloop.revcounter: Возвращает индекс цикла в обратном порядке, при этом конечный индекс равен 1.
  • forloop.revcounter0: Возвращает индекс цикла в обратном порядке, при этом последний индекс равен 0.
  • forloop.first (обычно используется с тегом if): возвращает True для первого элемента, False для остальных.
  • forloop.last (обычно используется с тегом if): возвращает True для последнего элемента, False для остальных.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
     views_list = ["a", "b", "c", "d", "e"]
     return render(request, "example.html", {"listvar": views_list})

Код файла HelloWorld/templates/example.html:

{% for i in listvar %} {{ forloop.counter }} {{ forloop.counter0 }} {{ forloop.revcounter }} {{ forloop.revcounter0 }} {{ forloop.first }} {{ forloop.last }} {% endfor %}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

{% empty %}

Необязательная конструкция {% empty %}: она выполняется, когда цикл пуст (т. е. когда логическое значение аргумента после in равно False).

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
  views_list = []
  return render(request, "example.html", {"listvar": views_list})

Код файла HelloWorld/templates/example.html:

{% for i in listvar %} {{ forloop.counter0 }} {% empty %} ПустоПустоНапримертоже~ {% endfor %}

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:

Тег {% for %} может быть вложенным:

{% for athlete in athlete_list %}
    <h1>{{ athlete.name }}</h1>
    <ul>
    {% for sport in athlete.sports_played %}
        <li>{{ sport }}</li>
    {% endfor %}
    </ul>
{% endfor %}

Тег комментария

В Django комментарии используют {# #}.

{# This is a comment #}

тег include

Тег {% include %} позволяет включать содержимое других шаблонов в шаблон.

В следующем примере включается шаблон nav.html:

{% include "nav.html" %}

csrf_token

csrf_token используется в формах, а его функция — защита от межсайтовой подделки запросов.

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

При использовании тега{% csrf_token %}: это сработает только при отправке данных через форму.

Анализ:

Сначала отправьте запрос на сервер, чтобы получить страницу входа. В этот момент middleware csrf автоматически сгенерирует скрытый тег input. Атрибут value в этом теге — случайная строка. Когда пользователь получает страницу входа, он также получает этот скрытый тег input.

Затем, когда пользователю нужно использовать форму для отправки данных, он передаст этот тег input и отправит его в middleware CSRF. Причина в том, что когда форма отправляет данные, она включает все теги input. Когда middleware CSRF получает данные, он определяет, является ли эта случайная строка той, которую он отправил пользователю в первый раз. Если да, отправка данных успешна; если нет, возвращается ошибка прав доступа 403.


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

1. Создайте в каталоге приложенияtemplatetagsкаталог (на том же уровне, что и каталог templates, имя каталога может быть только templatetags).

HelloWorld/
|-- HelloWorld
|   |-- __init__.py
|   |-- __init__.pyc
|   |-- settings.py
...
|-- manage.py
`-- templatetags
`-- templates

2. Создайте любой py-файл в каталоге templatetags, например:my_tags.py。

3. Код файла my_tags.py выглядит следующим образом:

from django import template

register = template.Library()   #register的名字是固定的,不可改变

Измените конфигурацию параметра TEMPLATES в файле settings.py и добавьте конфигурацию libraries:

Конфигурационный файл settings.py

...
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [BASE_DIR, "/templates",],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
            "libraries":{                          # Добавьте эти три строки конфигурации здесь
                'my_tags':'templatetags.my_tags'   # Добавьте эти три строки конфигурации здесь
            }                                      # Добавьте эти три строки конфигурации здесь
        },
    },
]
...

4. Используйте декоратор @register.filter для определения пользовательского фильтра.

Примечание:Декоратор может иметь не более 2 параметров.

@register.filter
def my_filter(v1, v2):
    return v1 * v2

5. Используйте декоратор @register.simple_tag для определения пользовательского тега.

@register.simple_tag
def my_tag1(v1, v2, v3):
    return v1 * v2 * v3

6. Перед использованием пользовательских тегов и фильтров необходимо импортировать этот py-файл в самом начале body в HTML-файле.

{% load my_tags %}

7. Используйте пользовательские фильтры в HTML.

{{ 11|my_filter:22 }}

8. Используйте пользовательские теги в HTML.

{% my_tag1 11 22 33 %}

9. Семантические теги

Импортируйте mark_safe в этом py-файле.

from django.utils.safestring import mark_safe

При определении тегов используйте метод mark_safe, чтобы сделать теги семантическими, что эквивалентно методу html() в jQuery.

Это даёт тот же эффект, что и фильтр safe в HTML-файлах фронтенда.

@register.simple_tag
def my_html(v1, v2):
    temp_html = "<input type='text' id='%s' class='%s' />" %(v1, v2)
    return mark_safe(temp_html)

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

{% my_html "zzz" "xxx" %}

Настройка статических файлов

1. Создайте каталог statics в корневом каталоге проекта.

2. Добавьте следующую конфигурацию в нижней части файла settings:

STATIC_URL = '/static/' # 别名 
STATICFILES_DIRS = [ 
    os.path.join(BASE_DIR, "statics"), 
]

3. Создайте каталоги css, js, images и plugins в каталоге statics для хранения CSS-файлов, JS-файлов, изображений и плагинов соответственно.

4. Поместите фреймворк bootstrap в каталог plugins.

5. Подключите bootstrap в теге head HTML-файла.

Примечание:На этом этапе в пути ссылки следует использовать псевдоним static из файла конфигурации, а не каталог statics.

<link rel="stylesheet" href="../static/plugins/bootstrap-3.3.7/dist/css/bootstrap.css.html">

Чтобы использовать это в шаблонах, нужно добавить{% load static %}код. В следующем примере мы импортируем изображение из каталога static.

Код файла HelloWorld/HelloWorld/views.py:

from django.shortcuts import render

def example(request):
    name ="Example"
    return render(request, "example.html", {"name": name})

Код файла HelloWorld/templates/example.html:

{% load static %} {{name}}<img src="{% static 'images/example-logo.png' %}" alt="example-logo">

Снова посетите http://127.0.0.1:8000/example, и вы увидите страницу:


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

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

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

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

родительский шаблон

Тег block...endblock:Зарезервированные блоки в родительском шаблоне оставлены для того, чтобы дочерний шаблон заполнил их различающимся содержимым. Разные зарезервированные блоки не должны иметь одинаковое имя.

{% block 名称 %} 
预留给子模板的区域,可以设置设置默认内容
{% endblock 名称 %}

дочерний шаблон

Дочерний шаблон использует тег extends для наследования родительского шаблона:

{% extends "父模板路径"%} 

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

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

{ % block 名称 % }
内容 
{% endblock 名称 %}

Затем мы сначала создаём файл base.html в каталоге templates предыдущего проекта. Код выглядит следующим образом:

Код файла HelloWorld/templates/base.html:

<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <title>Пример (example.com)</title> </head> <body> <h1>Hello World!</h1> <p>Пример теста Django.</p> {% block mainbody %} <p>оригинал</p> {% endblock %} </body> </html>

В приведённом выше коде блок с именем mainbody — это часть, которую могут заменить наследники.

Все теги {% block %} сообщают шаблонному движку, что дочерние шаблоны могут переопределять эти части.

В example.html наследуйте base.html и замените определённые блоки. Изменённый код example.html выглядит следующим образом:

Код файла HelloWorld/templates/example.html:

{%extends "base.html" %} {% block mainbody %} <p> Унаследованныйbase.htmlфайл</p> {% endblock %}

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

Снова перейдите по адресу http://127.0.0.1:8000/example, вывод следующий:

Другие расширения