Блок 1 — Python Basic (1–6)
- Лекция 1. Введение. Типизации. Переменные. Строки и числа. Булева алгебра. Ветвление
- Лекция 2. Обработка исключений. Списки, строки детальнее, срезы, циклы.
- Лекция 3. None. Range, list comprehension, sum, max, min, len, sorted, all, any. Работа с файлами
- Лекция 4. Хэш-таблицы. Set, frozenset. Dict. Tuple. Немного об импортах. Namedtuple, OrderedDict
- Лекция 5. Функции, типизация, lambda. Map, zip, filter.
- Лекция 6. Алгоритмы и структуры данных
Блок 2 — Git (7–8)
Блок 3 — Python Advanced (9–14)
- Лекция 9. Введение в ООП. Основные парадигмы ООП. Классы и объекты. Множественное наследование.
- Лекция 10. Magic methods. Итераторы и генераторы.
- Лекция 11. Imports. Standard library. PEP 8
- Лекция 12. Декораторы. Декораторы с параметрами. Декораторы классов (staticmethod, classmethod, property)
- Лекция 13. Тестирование
- Лекция 14. Проектирование. Паттерны. SOLID.
Блок 4 — SQL (15–17)
Блок 5 — Django (19–26)
- Лекция 19. Знакомство с Django
- ▶ Лекция 20. Templates. Static
- Лекция 21. Модели. Связи. Meta. Abstract, proxy.
- Лекция 22. Django ORM.
- Лекция 23. Forms, ModelForms. User, Authentication.
- Лекция 24. ClassBaseView
- Лекция 25. NoSQL. Куки, сессии, кеш
- Лекция 26. Логирование. Middleware. Signals. Messages. Manage commands
Блок 6 — Django Rest Framework (27–30)
Блок 7 — Python async (31–33)
Блок 8 — Deployment (34–35)
Что же такое шаблон? В бытовом понимании — это заготовка под что-то, что потом будет использоваться, в Django это почти так же.
Шаблонами мы называем заготовленные HTML-страницы, в которые мы можем добавить необходимые нам данные и логику.
Но как это работает?
Откроем начатый проект с прошлого занятия.
Создадим новую папку на уровне корня проекта и назовём её templates (название может быть любым, но принято называть именно так). Структура проекта:
blog_project/
blog_project/
settings.py
urls.py
...
blog/
views.py
...
templates/
manage.py
Чтобы обрабатывать шаблоны, мы должны «рассказать» Django, где именно искать эти самые шаблоны. Для этого нужно открыть blog_project/settings.py и отредактировать переменную TEMPLATES.
В ключ DIRS добавим нашу папку с шаблонами:
# blog_project/settings.py
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR / "templates"], # <-- добавляем путь к папке templates
'APP_DIRS': True,
'OPTIONS': {
# ...
},
},
]Для шаблонов приложений удобно использовать неймспейсы:
blog/templates/blog/home.htmlи вызыватьrender(request, 'blog/home.html').
Ключи:
BACKEND: путь к классу, который отвечает за обработку шаблонов. (Замена требуется очень редко.)DIRS: список папок, в которых Django будет искать шаблоны.APP_DIRS: еслиTrue, Django также ищет шаблоны в папкахtemplatesвнутри каждого приложения (например,blog/templates/).OPTIONS: дополнительные настройки.
Мы «рассказали» Django, где искать шаблоны, но пока ни одного не создали. Давайте сделаем это!
В папке templates создадим файл home.html:
blog_project/
blog_project/
blog/
templates/
home.html
manage.py
Содержимое файла home.html:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>Мой блог</title>
</head>
<body>
<h1>Добро пожаловать в блог!</h1>
</body>
</html>Итак, теперь у нас есть шаблон, но мы его не используем. Давайте переделаем нашу view для обработки шаблонов.
Рендеринг (rendering) — это процесс преобразования шаблона в готовый HTML, который будет отправлен браузеру.
Когда мы «рендерим» шаблон, Django:
- Находит файл шаблона по указанному пути
- Подставляет переданные данные (контекст) вместо переменных в шаблоне
- Выполняет логику шаблона (циклы, условия)
- Возвращает готовую HTML-строку
Для рендеринга используется функция render() из django.shortcuts:
# blog/views.py
from django.shortcuts import render
def home(request):
return render(request, 'home.html')Функция render() принимает:
request— объект запроса (обязательно)template_name— путь к шаблону относительно папкиtemplatescontext— словарь с данными для шаблона (опционально)
Функция
render()возвращает объектHttpResponseс отрендеренным HTML.
Перезапустим сервер и увидим результат на главной странице http://127.0.0.1:8000/.
Мы отрендерили шаблон! Но в нём нет никаких переданных данных. Для передачи данных нужно добавить третий аргумент — словарь (context):
# blog/views.py
from django.shortcuts import render
def home(request):
return render(request, 'home.html', {
'site_name': 'Мой блог',
'articles_count': 42,
})Для вывода данных в Django-шаблоне используются двойные фигурные скобки {{ }}:
<h1>{{ site_name }}</h1>
<p>Всего статей: {{ articles_count }}</p>Для доступа к вложенным структурам используется точка:
{{ article.title }} {# атрибут объекта #}
{{ article.author.username }} {# вложенный атрибут #}
{{ topics.0 }} {# первый элемент списка #}
{{ config.debug }} {# значение словаря #}Пример с передачей разных типов данных:
# blog/views.py
def home(request):
# Пока у нас нет моделей, используем словари для имитации данных
articles = [
{'title': 'Первая статья', 'author': 'Иван'},
{'title': 'Вторая статья', 'author': 'Мария'},
]
return render(request, 'home.html', {
'site_name': 'Мой блог',
'articles': articles,
'topics': ['Python', 'Django', 'Web'],
}){# templates/home.html #}
<h1>{{ site_name }}</h1>
<p>Первая тема: {{ topics.0 }}</p>
<p>Автор первой статьи: {{ articles.0.author }}</p>Для логических условий и циклов используются специальные скобки {% %}.
После всех логических операторов и циклов в шаблонах нужно ставить соответствующий закрывающий тег! Например,
{% for ...%} {% endfor %},{% if ... %} {% endif %}.
В шаблонах можно использовать условную логику. Это полезно, например, чтобы показывать разный контент залогиненным и незалогиненным пользователям:
{% if user.is_authenticated %}
<p>Добро пожаловать, {{ user.username }}!</p>
<a href="{% url 'logout' %}">Выйти</a>
{% else %}
<a href="{% url 'login' %}">Войти</a>
<a href="{% url 'register' %}">Регистрация</a>
{% endif %}Ещё пример — показываем кнопку редактирования только автору статьи:
{% if article.author == user %}
<a href="{% url 'article-update' article.id %}">Редактировать</a>
<a href="{% url 'article-delete' article.id %}">Удалить</a>
{% endif %}Отступы в шаблонах не имеют значения для Django, но делают код читаемее.
Для множественных условий используется {% elif %}:
{% if user.is_superuser %}
<span class="badge">Администратор</span>
{% elif user.is_staff %}
<span class="badge">Модератор</span>
{% elif user.is_authenticated %}
<span>Добро пожаловать, {{ user.username }}!</span>
{% else %}
<a href="{% url 'login' %}">Войти</a>
{% endif %}Также можно использовать логические операторы and, or, not:
{% if user.is_authenticated and user.is_active %}
<p>Активный пользователь</p>
{% endif %}
{% if not items %}
<p>Список пуст</p>
{% endif %}
{% if category == 'news' or category == 'articles' %}
<p>Контент</p>
{% endif %}В шаблонах можно использовать только цикл for — цикла while не существует.
Пример — выводим список статей на главной странице:
{# templates/home.html #}
<h1>Все статьи</h1>
<ul>
{% for article in articles %}
<li>
<a href="{% url 'article-detail' article.id %}">{{ article.title }}</a>
<span>— {{ article.author }}</span>
</li>
{% endfor %}
</ul>Напоминание: логика через
{% %}, данные через{{ }}
Вложенный цикл — выводим темы для каждой статьи:
{% for article in articles %}
<div class="article">
<h2>{{ article.title }}</h2>
<div class="topics">
{% for topic in article.topics %}
<span class="badge">{{ topic }}</span>
{% endfor %}
</div>
</div>
{% endfor %}Внутри цикла доступны следующие переменные:
| Переменная | Описание |
|---|---|
forloop.counter |
Номер текущей итерации (начиная с 1) |
forloop.counter0 |
Номер текущей итерации (начиная с 0) |
forloop.revcounter |
Номер итерации с конца (до 1) |
forloop.revcounter0 |
Номер итерации с конца (до 0) |
forloop.first |
True, если это первая итерация |
forloop.last |
True, если это последняя итерация |
forloop.parentloop |
Ссылка на родительский цикл (для вложенных циклов) |
Пример использования:
<ul>
{% for article in articles %}
<li class="{% if forloop.first %}first{% endif %} {% if forloop.last %}last{% endif %}">
{{ forloop.counter }}. {{ article.title }}
</li>
{% endfor %}
</ul>Тег {% empty %} позволяет отобразить альтернативный контент, если список пуст:
<ul>
{% for article in articles %}
<li>{{ article.title }}</li>
{% empty %}
<li>Статей пока нет</li>
{% endfor %}
</ul>Это эквивалентно:
{% if articles %}
<ul>
{% for article in articles %}
<li>{{ article.title }}</li>
{% endfor %}
</ul>
{% else %}
<p>Статей пока нет</p>
{% endif %}Но {% empty %} — более лаконичный и читаемый способ.
На самом деле все ключевые слова, используемые внутри {% %}, называются template tags, и их существует огромное
множество.
Их очень много, часть мы рассмотрим, часть вам придётся изучить самостоятельно.
Тег url позволяет сгенерировать URL по его имени. Это очень удобно: если адрес меняется, а его имя остаётся прежним, ссылки продолжат работать.
Примеры использования в блоге:
{# Ссылка на главную #}
<a href="{% url 'home' %}">Главная</a>
{# Ссылка на статью с параметром #}
<a href="{% url 'article-detail' article.id %}">{{ article.title }}</a>
{# Ссылка на тему #}
<a href="{% url 'topic-detail' topic.id %}">{{ topic.name }}</a>
{# Ссылка на профиль #}
<a href="{% url 'profile' %}">Мой профиль</a>Наследование шаблонов позволяет не дублировать общие части страниц (шапка, навигация, подвал). Вы создаёте базовый шаблон с «дырками» (блоками), а дочерние шаблоны заполняют эти блоки своим контентом.
Создадим базовый шаблон для нашего блога:
templates/base.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>{% block title %}Мой блог{% endblock %}</title>
{% block extra_css %}{% endblock %}
</head>
<body>
<header>
<nav>
<a href="{% url 'home' %}">Главная</a>
<a href="{% url 'topics' %}">Темы</a>
{% if user.is_authenticated %}
<a href="{% url 'my-feed' %}">Моя лента</a>
<a href="{% url 'profile' %}">Профиль</a>
<a href="{% url 'logout' %}">Выйти</a>
{% else %}
<a href="{% url 'login' %}">Войти</a>
<a href="{% url 'register' %}">Регистрация</a>
{% endif %}
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2024 Мой блог</p>
</footer>
{% block extra_js %}{% endblock %}
</body>
</html>Теперь создадим страницы, которые наследуются от базового шаблона:
templates/home.html
{% extends 'base.html' %}
{% block title %}Главная — Мой блог{% endblock %}
{% block content %}
<h1>Все статьи</h1>
<ul>
{% for article in articles %}
<li><a href="{% url 'article-detail' article.id %}">{{ article.title }}</a></li>
{% empty %}
<li>Статей пока нет</li>
{% endfor %}
</ul>
{% endblock %}templates/about.html
{% extends 'base.html' %}
{% block title %}О нас — Мой блог{% endblock %}
{% block content %}
<h1>О нашем блоге</h1>
<p>Это учебный проект для изучения Django.</p>
{% endblock %}Что произошло?
Мы создали базовый шаблон base.html, в котором описали общую структуру всех страниц: навигацию, шапку, подвал. Тег {% block content %} определяет «дырку», которую дочерние шаблоны заполняют своим контентом.
В home.html мы указали {% extends 'base.html' %} — это означает, что шаблон наследуется от базового. Затем мы переопределили блоки title и content.
Блоков может быть сколько угодно, главное — давать им разные имена.
Иногда нужно не заменить содержимое родительского блока, а дополнить его. Для этого используется {{ block.super }}:
{# base.html #}
{% block content %}
<div class="container">
{% endblock %}
{# child.html #}
{% extends 'base.html' %}
{% block content %}
{{ block.super }}
<h1>Мой контент</h1>
</div>
{% endblock %}Результат будет содержать и <div class="container"> из родителя, и <h1> из дочернего шаблона.
Тег include позволяет вставить один шаблон внутрь другого. Это полезно для переиспользуемых компонентов: карточка статьи, форма комментария, блок навигации.
Создадим компонент карточки статьи:
templates/components/article_card.html
<article class="article-card">
<h2><a href="{% url 'article-detail' article.id %}">{{ article.title }}</a></h2>
<p class="meta">
Автор: {{ article.author }} |
{% for topic in article.topics %}
<a href="{% url 'topic-detail' topic.id %}">{{ topic.name }}</a>{% if not forloop.last %}, {% endif %}
{% endfor %}
</p>
<p>{{ article.content|truncatechars:100 }}</p>
</article>Теперь используем этот компонент на главной странице:
templates/home.html
{% extends 'base.html' %}
{% block content %}
<h1>Все статьи</h1>
{% for article in articles %}
{% include 'components/article_card.html' %}
{% empty %}
<p>Статей пока нет</p>
{% endfor %}
{% endblock %}Можно передать переменные в подключаемый шаблон с помощью with:
{% include 'components/article_card.html' with article=featured_article %}Пример — компонент кнопки подписки на тему:
templates/components/subscribe_button.html
{% if user.is_authenticated %}
{% if is_subscribed %}
<a href="{% url 'topic-unsubscribe' topic.id %}">Отписаться</a>
{% else %}
<a href="{% url 'topic-subscribe' topic.id %}">Подписаться</a>
{% endif %}
{% endif %}Использование:
{% include 'components/subscribe_button.html' with topic=topic is_subscribed=True %}{# Однострочный комментарий #}
{% comment "Описание" %}
Многострочный комментарий.
Этот код не будет отображаться.
{% endcomment %}Тег {% csrf_token %} обязателен для всех POST-форм. Он защищает от CSRF-атак:
<form method="post" action="{% url 'article-create' %}">
{% csrf_token %}
<input type="text" name="title">
<textarea name="content"></textarea>
<button type="submit">Создать статью</button>
</form>Без
{% csrf_token %}POST-запросы будут отклонены с ошибкой 403 Forbidden.
Фильтры позволяют преобразовать данные перед отображением. Синтаксис: {{ переменная|фильтр }}
Примеры для блога:
{# Обрезаем текст статьи до 100 символов #}
<p>{{ article.content|truncatechars:100 }}</p>
{# Форматируем дату создания #}
<span>{{ article.created_at|date:"d.m.Y" }}</span>
{# Показываем темы через запятую #}
<span>Темы: {{ article.topic_names|join:", " }}</span>
{# Значение по умолчанию #}
<p>Автор: {{ article.author.username|default:"Аноним" }}</p>| Фильтр | Описание | Пример |
|---|---|---|
default |
Значение по умолчанию, если переменная пустая | {{ value|default:"ничего" }} |
length |
Длина строки или списка | {{ articles|length }} |
truncatechars |
Обрезает строку до N символов | {{ text|truncatechars:50 }} |
truncatewords |
Обрезает строку до N слов | {{ text|truncatewords:10 }} |
date |
Форматирование даты | {{ created|date:"d.m.Y" }} |
time |
Форматирование времени | {{ created|time:"H:i" }} |
join |
Объединяет список в строку | {{ tags|join:", " }} |
add |
Сложение чисел или конкатенация | {{ value|add:5 }} |
capfirst |
Первая буква заглавная | {{ name|capfirst }} |
lower |
Все буквы строчные | {{ name|lower }} |
upper |
Все буквы заглавные | {{ name|upper }} |
title |
Каждое слово с заглавной | {{ name|title }} |
slugify |
Преобразует в slug | {{ title|slugify }} |
linebreaks |
Переносы строк в <p> и <br> |
{{ text|linebreaks }} |
linebreaksbr |
Переносы строк в <br> |
{{ text|linebreaksbr }} |
yesno |
Преобразует True/False/None | {{ flag|yesno:"да,нет,неизвестно" }} |
pluralize |
Склонение для чисел | {{ count }} item{{ count|pluralize }} |
Примеры использования:
{# Обрезка текста #}
<p>{{ article.content|truncatewords:30 }}...</p>
{# Длина списка #}
<p>Всего статей: {{ articles|length }}</p>
{# Сложение #}
<p>Следующая страница: {{ page|add:1 }}</p>
{# Форматирование даты #}
<p>Опубликовано: {{ article.created_at|date:"d F Y" }}</p>Полный список встроенных фильтров можно посмотреть в документации.
Django автоматически экранирует HTML в переменных для защиты от XSS-атак (Cross-Site Scripting). Это означает, что если пользователь введёт <script>alert('hack')</script>, это будет отображено как текст, а не выполнено как код.
{# Если user_input = "<script>alert('XSS')</script>" #}
{{ user_input }}
{# Результат: <script>alert('XSS')</script> #}Если вы уверены, что HTML безопасен (например, это контент из WYSIWYG-редактора администратора), используйте фильтр |safe:
{{ article.content|safe }}
⚠️ Внимание! Используйте|safeтолько для доверенного контента. Никогда не применяйте его к пользовательскому вводу!
Фильтр |escape явно экранирует HTML (полезно, если автоэкранирование отключено):
{{ user_input|escape }}Можно управлять автоэкранированием для блока кода:
{% autoescape off %}
{# Здесь автоэкранирование отключено #}
{{ trusted_html }}
{% endautoescape %}
{% autoescape on %}
{# Здесь автоэкранирование включено (по умолчанию) #}
{{ user_input }}
{% endautoescape %}Удаляет все HTML-теги из строки:
{{ user_input|striptags }}
{# "<p>Hello</p>" → "Hello" #}На самом деле, стандартным набором дело не ограничивается, и в случае необходимости можно дописать свои теги и фильтры. Подробнее об этом можно почитать в документации.
Прежде чем перейти к рассмотрению template tag, давайте разберёмся, что такое статические файлы. В контексте веб-разработки статические файлы — это те файлы, которые не изменяются на сервере, а напрямую передаются пользователю. К ним относятся:
- CSS-файлы, отвечающие за стилизацию веб-страниц.
- JavaScript-файлы, добавляющие интерактивность страницам.
- Изображения, иконки, шрифты и другие медиафайлы.
В Django эти файлы не хранятся в базе данных и не генерируются динамически, как HTML-контент. Они должны быть доступны для всех клиентов в неизменном виде.
Прежде чем мы сможем использовать {% static %}, необходимо правильно настроить Django для работы со статическими
файлами. Основные шаги включают:
- Указание директории для хранения статических файлов.
- Настройка URL для доступа к статическим файлам.
В файле settings.py вы найдете (или добавите) следующие параметры:
-
STATIC_URL: Этот параметр определяет URL, по которому будут доступны статические файлы. Обычно это/static/. -
Убедитесь, что приложение 'django.contrib.staticfiles' есть в INSTALLED_APPS (в новых проектах добавляется по умолчанию).
STATIC_URL = '/static/'
Этот параметр, скорее всего, указан автоматически, он отвечает за то, чтобы ваши статические файлы можно было бы получить по адресу
http://127.0.0.1:8000/static/. На самом деле там много деталей и нюансов, но их мы будем рассматривать гораздо позже.
Для того чтобы у вас отрабатывали статические файлы, нужно чтобы у вас в
settings.pyбыл указанDEBUG=True, что это и зачем — опять же дальше по курсу.
-
STATICFILES_DIRS: Этот параметр указывает на дополнительные директории, в которых Django будет искать статические файлы. Это полезно, если у вас есть несколько источников для статических файлов.STATICFILES_DIRS = [BASE_DIR / "static"]
project/
static/
css/styles.css
templates/
base.html
Просто указываете папку, куда вы сложили свои статические файлы.
-
STATIC_ROOT: Этот параметр используется для указания директории, куда Django будет собирать все статические файлы при выполнении командыcollectstatic. Это полезно для деплоя на продакшн сервер.STATIC_ROOT = BASE_DIR / "staticfiles"
Этот параметр вам пока не нужен! Он используется для запуска приложений на реальных серверах.
Теперь, когда мы настроили Django для работы со статическими файлами, давайте рассмотрим, как используется template
tag {% static %}. Этот тег позволяет нам генерировать правильные URL для статических файлов в шаблонах.
Прежде чем использовать тег {% static %}, необходимо загрузить соответствующую библиотеку в вашем шаблоне. Это
делается с помощью тега {% load static %}. Этот тег должен быть расположен в верхней части вашего шаблона, как
правило, до использования {% static %}.
Обычно записывается в самом верху, по аналогии с
importв Python.
Пример:
{% load static %}Синтаксис тега {% static %} выглядит следующим образом:
{% static 'path/to/your/static/file.ext' %}Здесь 'path/to/your/static/file.ext' — это путь к файлу относительно одной из директорий, указанных
в STATICFILES_DIRS.
Предположим, у вас есть файл styles.css, находящийся в директории static/css/. Чтобы подключить этот файл в шаблоне,
вам нужно использовать следующий код:
{% load static %}
<link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}">Django автоматически преобразует это в правильный URL, который будет выглядеть как /static/css/styles.css.
Тег {% static %} можно использовать не только для CSS, но и для других типов файлов, например, изображений или
JavaScript:
{% load static %}
<img src="{% static 'images/logo.png' %}" alt="Logo">
<script src="{% static 'js/main.js' %}"></script>-
Всегда загружайте библиотеку static. Не забывайте добавлять
{% load static %}в начало ваших шаблонов, где используются статические файлы. -
Не хардкодьте пути к статическим файлам. Всегда используйте
{% static %}для создания ссылок на статические файлы. -
Организуйте свои статические файлы логически. Размещайте их в соответствующих поддиректориях, например:
css,js,images.
Преимущество использования {% static %} заключается в том, что он обеспечивает правильное разрешение путей к
статическим файлам независимо от того, где они находятся. Это особенно важно при деплое приложения, когда может
измениться базовый URL для статических файлов.
Продолжаем работу над проектом блога. На этом этапе страницы всё ещё заполнены абстрактным контентом — работать должен только переход между страницами.
-
Создайте базовый шаблон
base.htmlс общей структурой:- Навигация (ссылки на главную, список тем, профиль и т.д.)
- Место для основного контента (
{% block content %}) - Подвал страницы
-
Все остальные страницы должны наследоваться от
base.html. Создайте шаблоны для всех URL, у которых в задании указано слово «страница»:- Главная страница (
/) - Моя лента (
/my-feed/) - Страница статьи (
/<article_id>/) - Страница редактирования статьи (
/<article_id>/update/) - Страница создания статьи (
/create/) - Список тем (
/topics/) - Страница темы (
/topics/<topic_id>/) - Профиль (
/profile/) - Регистрация (
/register/) - Смена пароля (
/set-password/) - Вход (
/login/) - Статьи по дате (
/<year>/<month>/)
- Главная страница (
-
У всех страниц должен быть разный
<title>— используйте блок{% block title %}. -
Используйте тег
{% url %}для всех ссылок между страницами. -
(Опционально) Добавьте CSS-файл для базовой стилизации и подключите его через
{% static %}.
На данном этапе контент страниц может быть заглушкой (placeholder). Главное — правильная структура шаблонов и работающая навигация.
← Лекция 19: Знакомство с Django | Лекция 21: Модели. Связи. Meta. Abstract, proxy. →





