Представления и шаблоны — Рендеринг списка статей

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


Роль представлений (View)

Представления — это основной логический слой Django, отвечающий за:

  • Приём пользовательских запросов (requestпараметр)
  • Запрос необходимых данных из базы данных
  • Передача данных в шаблоны для рендеринга HTML и возврат их пользователю.

Django-представления можно писать двумя способами:Функциональное представление (FBV)и представления на основе классов (CBV).

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


Основы функциональных представлений

Функция представления Django получаетrequestПараметры и возвращаемые значенияHttpResponseОбъект.

Пример

# Путь к файлу: blog/views.py
from django.shortcuts import render
from .models import Post

def index(request):
    """Главная страница блога: отображает все статьи"""
    # Запрос всех статей из базы данных (в обратном хронологическом порядке)
    posts = Post.objects.all().order_by('-created_at')

    # Три параметра render:
    # 1. request — объект запроса
    # 2. Путь к шаблону — blog/index.html
    # 3. словарь context — данные, передаваемые в шаблон
    context = {
        'posts': posts,
        'title': 'EXAMPLE Blog - Главная'
    }
    return render(request, 'blog/index.html', context)

render()Это наиболее часто используемая сокращённая функция в Django, объединяющая: загрузку шаблонов → внедрение данных → генерацию HTML → возврат ответов.


Синтаксис шаблонов Django

Синтаксис шаблонов Django очень похож на синтаксис шаблонов Vue3, но есть несколько ключевых различий.

НазначениеШаблоны DjangoШаблон Vue3
Вывод переменных{{ variable }}{{ variable }}
Циклы{% for item in list %}...{% endfor %}v-for="item in list"
Условия{% if cond %}...{% endif %}v-if="cond"
Генерация URL{% url 'name' %}router-link to="name"
Наследование макета{% extends 'base.html' %}Вложенность компонентов

Ключевое различие: шаблоны Django используют{% %}Обёртывающие управляющие теги; Vue3 используетv-директивы. В шаблонах Django,серверныйрендеринг—HTML генерируется, а затем отправляется в браузер; Vue3 выполняетбраузерныйрендеринг—браузер получает JS, а затем собирает HTML.


Наследование шаблонов: base.html

Когда несколько страниц используют одну и ту же панель навигации и нижний колонтитул, используйтенаследование шаблонов.Избегайте дублирования.

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

Создайте родительский шаблон base.html

Пример

<!-- Путь к файлу: blog/templates/blog/base.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}EXAMPLE Blog{% endblock %}</title>
    <style>
        * { margin: 0; padding: 0; box-sizing: border-box; }
        body {
            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
            background: #f5f5f5;
            color: #333;
        }
        .navbar {
            display: flex; justify-content: space-between; align-items: center;
            padding: 16px 40px; background: #fff;
            box-shadow: 0 2px 8px rgba(0,0,0,0.1);
        }
        .navbar .logo { font-size: 22px; font-weight: bold; color: #2c3e50; text-decoration: none; }
        .navbar a { margin-left: 20px; text-decoration: none; color: #333; }
        .container { max-width: 960px; margin: 40px auto; padding: 0 20px; }
        .footer { text-align: center; padding: 20px; color: #999; border-top: 1px solid #eee; }
        .section-title { font-size: 24px; margin-bottom: 20px; }
    </style>
    {% block extra_head %}{% endblock %}
</head>
<body>
    <!-- Панель навигации -->
    <header class="navbar">
        <a href="/" class="logo">ПРИМЕР Блог</a>
        <nav>
            <a href="/">Главная</a>
            <a href="#">О нас</a>
        </nav>
    </header>

    <!-- Основная область содержимого (заполняется дочерним шаблоном) -->
    <main class="container">
        {% block content %}
        {% endblock %}
    </main>

    <!-- Подвал -->
    <footer class="footer">
        <p>© 2024 EXAMPLE Blog. Работает на Django.</p>
    </footer>
</body>
</html>

Создайте дочерний шаблон index.html

Пример

<!-- Путь к файлу: blog/templates/blog/index.html -->
<!-- extends должен быть в самом начале -->
{% extends 'blog/base.html' %}

{% block title %}{{ title }}{% endblock %}

{% block content %}
<h2 class="section-title">Последние статьи</h2>

{% if posts %}
    <div class="article-grid">
        {% for post in posts %}
        <div class="article-card">
            <div class="card-content">
                <span class="card-category">{{ post.category.name }}</span>
                <h3>{{ post.title }}</h3>
                <p>{{ post.summary|truncatechars:80 }}</p>
                <span class="card-date">{{ post.created_at|date:"Y-m-d" }}</span>
            </div>
        </div>
        {% endfor %}
    </div>
{% else %}
    <p class="empty-tip">Пока нет статей, пожалуйста, следите за обновлениями.</p>
{% endif %}
{% endblock %}

Несколько моментов, используемых в шаблонах:

СинтаксисЗначение
{% extends 'blog/base.html' %}Наследование родительского шаблона должно быть записано в первой строке
{% block content %}...{% endblock %}Заполните блок содержимого, определённый в родительском шаблоне.
{{ post.category.name }}Доступ к свойствам связанных объектов через внешние ключи
{{ post.summary|truncatechars:80 }}Фильтр: обрезать до первых 80 символов
{{ post.created_at|date:"Y-m-d" }}Фильтр: форматировать дату

В шаблонах Django,ФильтрИспользовать символ вертикальной черты|Указывает, ставится после переменной, используется для форматированного вывода. Распространённые фильтры:date、truncatechars、length、default。


Добавление стилей к списку статей

Добавьте следующий CSS в тег style в base.html:

Пример

/* Макет сетки карточек статей */
.article-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
    gap: 24px;
}

.article-card {
    background: #fff;
    border-radius: 12px;
    overflow: hidden;
    box-shadow: 0 2px 12px rgba(0,0,0,0.08);
    transition: transform 0.2s, box-shadow 0.2s;
}

.article-card:hover {
    transform: translateY(-4px);
    box-shadow: 0 8px 24px rgba(0,0,0,0.12);
}

.card-content {
    padding: 20px;
}

.card-category {
    display: inline-block;
    padding: 2px 10px;
    background: #E8EEF5;
    color: #3D71A6;
    border-radius: 12px;
    font-size: 12px;
    margin-bottom: 8px;
}

.article-card h3 {
    font-size: 18px;
    margin-bottom: 8px;
    color: #222;
}

.article-card p {
    font-size: 14px;
    color: #666;
    line-height: 1.6;
    margin-bottom: 12px;
}

.card-date {
    font-size: 12px;
    color: #999;
}

.empty-tip {
    text-align: center;
    color: #999;
    padding: 60px 0;
    font-size: 16px;
}

Практика: проверьте, как выглядит главная страница

Убедитесь, что выполнены следующие пункты:

  • Модели Post и Category определены в models.py и миграции выполнены
  • В админ-панели добавлено как минимум 3–5 статей (отнесённых к разным категориям).
  • Функция index в views.py запрашивает статьи из базы данных и передаёт их в шаблон.
  • Маршрут / в urls.py указывает на представление index

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


Итоги главы

В этой главе вы освоили основное взаимодействие представлений и шаблонов Django: views.py запрашивает данные из базы данных и передаёт их в контекст, render() отрисовывает шаблоны, родительский шаблон base.html определяет макет и блоки, дочерние шаблоны расширяют его и заполняют содержимое, а также синтаксис шаблонов Django ({{ }}, {% %}, фильтры).

Главная страница блога теперь управляется базой данных, а не задана жёстко в коде.

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