Рабoта с ListView, вывод списка статей

⏱ 25 мин · 🧩 заданий: 5

Главная страница

modules/blog/views.py:

from django.views.generic import ListView

from .models import Article, Category


class ArticleListView(ListView):
    model = Article
    template_name = "blog/articles_list.html"
    context_object_name = "articles"

    def get_queryset(self):
        return Article.custom.all()

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["title"] = "Главная страница"
        return context

Статьи по категории

class ArticleByCategoryListView(ListView):
    model = Article
    template_name = "blog/articles_list.html"
    context_object_name = "articles"
    category = None

    def get_queryset(self):
        self.category = Category.objects.get(slug=self.kwargs["slug"])
        queryset = Article.objects.filter(category__slug=self.category.slug)
        return queryset

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context["title"] = f"Статьи из категории: {self.category.title}"
        return context
  • Параметры из URL — в self.kwargs.
  • Найденную категорию сохраняем в self.category, чтобы использовать её в get_context_data без повторного запроса.
  • Один шаблон обслуживает обе страницы.

Чтобы показывать статьи и из подкатегорий, используйте дерево:

categories = self.category.get_descendants(include_self=True)
return Article.custom.all().filter(category__in=categories)

А для несуществующей категории правильнее вернуть 404: get_object_or_404(Category, slug=self.kwargs["slug"]).

Маршруты

modules/blog/urls.py:

from django.urls import path

from .views import ArticleByCategoryListView, ArticleListView

urlpatterns = [
    path("", ArticleListView.as_view(), name="home"),
    path("category/<str:slug>/", ArticleByCategoryListView.as_view(), name="articles_by_category"),
]

Конвертер str вместо slug — на случай, если слаги будут содержать кириллицу (об этом — в следующем уроке).

Шаблоны

Структура:

templates/
├── header.html
├── footer.html
├── main.html            ← базовый шаблон
├── src/                 ← статика (STATICFILES_DIRS)
│   ├── css/
│   └── js/
└── blog/
    └── articles_list.html

templates/main.html:

{% load static %}
<!doctype html>
<html lang="ru">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>{{ title }}</title>
</head>
<body>
  {% include "header.html" %}
  <div class="container">
    {% block content %}{% endblock %}
  </div>
  {% include "footer.html" %}
</body>
</html>

templates/blog/articles_list.html:

{% extends "main.html" %}

{% block content %}
  {% for article in articles %}
    <div class="card mb-3">
      <div class="row">
        <div class="col-4">
          {% if article.thumbnail %}
            <img src="{{ article.thumbnail.url }}" class="card-img-top" alt="{{ article.title }}">
          {% endif %}
        </div>
        <div class="col-8">
          <div class="card-body">
            <h5 class="card-title"><a href="{{ article.get_absolute_url }}">{{ article.title }}</a></h5>
            <a href="{{ article.category.get_absolute_url }}">{{ article.category.title }}</a>
            <p class="card-text">{{ article.short_description|safe|truncatechars:300 }}</p>
            <small class="text-muted">{{ article.time_create|date:"d.m.Y" }} · {{ article.author.username }}</small>
          </div>
        </div>
      </div>
    </div>
  {% empty %}
    <p>Статей пока нет.</p>
  {% endfor %}
{% endblock %}

Проверяйте наличие файла

{{ article.thumbnail.url }} у статьи без превью выбросит ValueError. Оборачивайте вывод в {% if article.thumbnail %} или задайте картинку по умолчанию.

Итог

  • ListView + get_queryset() с self.kwargs — список по параметру из URL.
  • Найденные в get_queryset объекты сохраняем в атрибуты, чтобы использовать в get_context_data.
  • Один шаблон списка для главной и для категорий.
  • Шаблоны собраны из main.html + include для шапки и подвала.

Практика решено 0 из 5

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Написать код

Напишите ArticleByCategoryListView (наследник ListView): атрибуты model = Article, template_name = "blog/articles_list.html", context_object_name = "articles". В get_queryset найдите категорию по self.kwargs["slug"] (сохраните в self.category) и верните статьи этой категории: Article.objects.filter(category__slug=self.category.slug).

Задание 2 Один вариант ответа

Где в CBV доступны именованные параметры из URL, например slug из <slug:slug>/?

Задание 3 Написать код

Напишите маршруты: "" → ArticleListView (имя home), category/<str:slug>/ → ArticleByCategoryListView (имя articles_by_category).

Задание 4 Один вариант ответа

Почему для категорий используется конвертер str, а не slug?

Задание 5 Написать код

В карточке статьи выведите превью только если оно есть: <img src="{{ article.thumbnail.url }}" alt="{{ article.title }}"> внутри {% if article.thumbnail %}...{% endif %}.

Войдите, чтобы отмечать пройденные уроки.