Динамические страницы в Django: шаблоны и рендеринг

Раздел: Фреймворки Python -> Веб-разработка на Django

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

Основные подходы к шаблонизации

Как создать единую структуру страниц с общими элементами?

Наиболее эффективное решение - наследование шаблонов через тег extends и блоки block. Это позволяет определить базовый каркас (header, footer, sidebar) и заполнять только изменяемое содержимое.


    <!-- base.html -->
    <!DOCTYPE html>
    <html>
    <head>
      <title>{% block title %}Default Title{% endblock %}</title>
    </head>
    <body>
      <header>Шапка сайта</header>
      <main>{% block content %}{% endblock %}</main>
      <footer>Подвал</footer>
    </body>
    </html>
  

Django python html (использование django с html шаблонами)

Дочерний шаблон:


    <!-- index.html -->
    {% extends "base.html" %}
    {% block title %}Главная страница{% endblock %}
    {% block content %}
      <h1>Добро пожаловать!</h1>
      <p>Сегодня {{ current_date|date:"d.m.Y" }}</p>
    {% endblock %}
  

View:


    from django.shortcuts import render
    from datetime import date

    def home(request):
        return render(request, 'index.html', {'current_date': date.today()})
  

Ошибки: если указан неверный путь к шаблону, возникает TemplateDoesNotExist. Решение - проверьте настройку TEMPLATES.DIRS или APP_DIRS. При использовании кэширования шаблонов в production не забывайте сбрасывать кэш после изменений.

Как вывести одну страницу с минимальной структурой?

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


    <!-- hello.html -->
    <h1>{{ greeting }}</h1>
  

    def hello(request):
        return render(request, 'hello.html', {'greeting': 'Привет, мир!'})
  

Как вставлять общие компоненты на разные страницы без наследования?

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


    <!-- menu.html -->
    <nav>
      <a href="/">Главная</a>
      <a href="/about">О нас</a>
    </nav>
  

    <!-- about.html -->
    {% include "menu.html" %}
    <h1>О компании</h1>
    <p>Информация...</p>
  

Как выполнить сложные вычисления или запросы внутри шаблона?

Пользовательские теги и фильтры позволяют вынести логику из шаблона в Python. Создайте файл templatetags/custom_tags.py в приложении.


    from django import template
    register = template.Library()

    @register.simple_tag
    def multiply(a, b):
        return a * b
  

В шаблоне:


    {% load custom_tags %}
    <p>Результат: {% multiply 5 7 %}</p>
  

Как обновлять часть страницы без перезагрузки?

Можно рендерить фрагмент шаблона и возвращать его в ответ на AJAX-запрос. Создайте отдельный шаблон для части страницы.


    <!-- comment_list.html -->
    <ul>
      {% for comment in comments %}
        <li>{{ comment.text }}</li>
      {% endfor %}
    </ul>
  

    def ajax_comments(request):
        comments = Comment.objects.all()
        return render(request, 'comment_list.html', {'comments': comments})
  

Типичные проблемы и их решение

  • Ошибка TemplateDoesNotExist: проверьте, что файл шаблона находится в правильной директории (в папке templates приложения или в общем каталоге, указанном в DIRS).
  • Кэширование шаблонов при разработке: установите в настройках DEBUG = True, чтобы шаблоны перезагружались при каждом запросе. В production используйте кэш, но не забывайте сбросить его после изменений.
  • Отсутствие переменной в контексте: если переменная не передана, шаблон молча пропускает тег {{ var }} или показывает пустую строку. Используйте фильтр default или проверку {% if var %}.
  • Проблемы со статическими файлами: настройте STATIC_URL и STATICFILES_DIRS. В шаблоне загружайте статику через {% load static %}, а ссылки через {% static 'css/style.css' %}.

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

Пример 1: Полное наследование с меню и футером

Базовый шаблон (base.html):

Пример

  <!DOCTYPE html>
  <html lang="ru">
  <head>
    <meta charset="UTF-8">
    <title>{% block title %}Мой сайт{% endblock %}</title>
    <link rel="stylesheet" href="{% static 'css/style.css' %}">
  </head>
  <body>
    <header>
      <nav>{% include "menu.html" %}</nav>
    </header>
    <main>{% block content %}{% endblock %}</main>
    <footer>2025 © Все права защищены</footer>
  </body>
  </html>

Menu (menu.html):

Пример

  <a href="{% url 'home' %}">Главная</a>
  <a href="{% url 'about' %}">О нас</a>
  <a href="{% url 'contact' %}">Контакты</a>

Дочерний шаблон (about.html):

Пример

  {% extends "base.html" %}
  {% block title %}О нас - Мой сайт{% endblock %}
  {% block content %}
    <h1>О нашей компании</h1>
    <p>Мы занимаемся разработкой веб-приложений на Django.</p>
  {% endblock %}

View (views.py):

Пример

  from django.shortcuts import render

  def about(request):
      return render(request, 'about.html')

Результат (сгенерированный HTML):

  <!DOCTYPE html>
  <html lang="ru">
  <head>
    <meta charset="UTF-8">
    <title>О нас - Мой сайт</title>
    <link rel="stylesheet" href="/static/css/style.css">
  </head>
  <body>
    <header>
      <nav>
        <a href="/">Главная</a>
        <a href="/about/">О нас</a>
        <a href="/contact/">Контакты</a>
      </nav>
    </header>
    <main>
      <h1>О нашей компании</h1>
      <p>Мы занимаемся разработкой веб-приложений на Django.</p>
    </main>
    <footer>2025 © Все права защищены</footer>
  </body>
  </html>

Пример 2: Использование цикла и фильтра даты

View передает список событий:

Пример

  def events(request):
      events = [
          {'title': 'Конференция', 'date': '2025-03-15', 'time': '10:00'},
          {'title': 'Семинар', 'date': '2025-04-01', 'time': '14:30'},
      ]
      return render(request, 'events.html', {'events': events})

Шаблон (events.html):

Пример

  <ul>
    {% for event in events %}
      <li>
        <strong>{{ event.title }}</strong>
        <span>{{ event.date|date:"d E Y" }} в {{ event.time }}</span>
      </li>
    {% empty %}
      <li>Мероприятия не найдены</li>
    {% endfor %}
  </ul>

Результат:

  <ul>
    <li>
      <strong>Конференция</strong>
      <span>15 марта 2025 в 10:00</span>
    </li>
    <li>
      <strong>Семинар</strong>
      <span>01 апреля 2025 в 14:30</span>
    </li>
  </ul>

Пример 3: Пользовательский inclusion_tag для вывода последних новостей

Код тега (templatetags/news_tags.py):

Пример

  from django import template
  from ..models import News
  register = template.Library()

  @register.inclusion_tag('news/latest_news.html')
  def latest_news(count=5):
      news = News.objects.order_by('-created_at')[:count]
      return {'news_list': news}

Шаблон для тега (news/latest_news.html):

Пример

  <ul>
    {% for item in news_list %}
      <li><a href="{{ item.get_absolute_url }}">{{ item.title }}</a></li>
    {% endfor %}
  </ul>

Использование в любом шаблоне:

Пример

  {% load news_tags %}
  <aside>
    <h3>Последние новости</h3>
    {% latest_news 3 %}
  </aside>

Результат (если есть новости):

  <aside>
    <h3>Последние новости</h3>
    <ul>
      <li><a href="/news/1/">Обновление версии 5.0</a></li>
      <li><a href="/news/2/">Новый курс по Django</a></li>
      <li><a href="/news/3/">Конференция разработчиков</a></li>
    </ul>
  </aside>

Пример 4: Обработка формы и вывод ошибок

View с формой:

Пример

  from django.shortcuts import render, redirect
  from .forms import ContactForm

  def contact(request):
      if request.method == 'POST':
          form = ContactForm(request.POST)
          if form.is_valid():
              # обработка
              return redirect('thanks')
      else:
          form = ContactForm()
      return render(request, 'contact.html', {'form': form})

Шаблон (contact.html):

Пример

  <form method="post">
    {% csrf_token %}
    {{ form.as_p }}
    {% if form.errors %}
      <div class="errors">
        {% for field in form %}
          {% for error in field.errors %}
            <p class="fw-bold">{{ error }}</p>
          {% endfor %}
        {% endfor %}
      </div>
    {% endif %}
    <button type="submit">Отправить</button>
  </form>

Результат при ошибке валидации:

  <form method="post">
    <input type="hidden" name="csrfmiddlewaretoken" value="...">
    <p><label for="id_name">Имя:</label> <input id="id_name" type="text" name="name" maxlength="100" required></p>
    <p><label for="id_email">Email:</label> <input id="id_email" type="email" name="email" required></p>
    <div class="errors">
      <p class="fw-bold">Обязательное поле.</p>
    </div>
    <button type="submit">Отправить</button>
  </form>

Использование Django с HTML шаблонами - comments

En
Django python html (python)