Рабoта с ListView, вывод списка статей
Главная страница
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
Напишите 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).
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
Где в CBV доступны именованные параметры из URL, например slug из <slug:slug>/?
Напишите маршруты: "" → ArticleListView (имя home), category/<str:slug>/ → ArticleByCategoryListView
(имя articles_by_category).
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?
В карточке статьи выведите превью только если оно есть: <img src="{{ article.thumbnail.url }}" alt="{{ article.title }}">
внутри {% if article.thumbnail %}...{% endif %}.
{% if article.thumbnail %}
<img src="{{ article.thumbnail.url }}" alt="{{ article.title }}">
{% endif %}
Войдите, чтобы отмечать пройденные уроки.