Django предоставляет несколько общих представлений на основе классов для выполнения общих задач. Одним из них является DetailView. Его следует использовать, когда вы хотите представить подробную информацию об экземпляре одной модели.
Если мы решим написать представление на View, то оно будет выглядеть примерно таким:
class Product(View):
def get(self, request, *args, **kwargs):
product = get_object_or_404(Product, pk=kwargs['pk'])
context = {'product': product}
return render(request, 'base/products_detail.html', context)
Как и в прошлом разделе, мы должны написать метод get в нашем классе, в котором мы должны указать запрос и в какой шаблон мы будем выводить результат.
Но проще всего будет работать через класс DetailView, который отвечает за просмотр отдельного объекта, рассмотрим его структуру:
DetailView наследует непосредственно от 1 представления и 1 миксина, но содержит в себе в общей сложности 2 представления и 4 миксина, которые дают этому представлению все атрибуты и методы, которые он имеет.
DetailView может идентифицировать объект(используется метод filter()), как по полю первичного ключа - pk, id, так и по полю слага - slug, приняв значения из параметров pk или slug передаваемых в URL-адресе.
Важными атрибутами класса DetailView являются slug_url_kwarg, pk_url_kwarg и slug_field.
slug_url_kwarg: задаёт имя параметра, передаваемого в URL-адресе, для идентификации по полю слага, значение по умолчанию'slug'.
pk_url_kwarg: задаёт имя параметра, передаваемого в URL-адресе, для идентификации по полю первичного ключа, значение по умолчанию'pk'.
slug_field: задаёт поле слага, значение по умолчанию'slug'.
query_pk_and_slug: для идентификации по полям слага и первичного ключа одновременно, значение по умолчаниюFalse.
Полный список атрибутов и методов мы можем посмотреть по этой ссылке.
Приступим к практике, перейдем в файл views.py приложения blog и создадим наше первое представление для вывода списка статей:
from django.views.generic import ListView, DetailView
from .models import Post
# код класса PostListView
class PostDetailView(DetailView):
model = Post
template_name = 'blog/post_detail.html'
context_object_name = 'post'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['title'] = self.object.title
return context
model- название нашей модели,Post.
template_name- По умолчаниюDetailViewищет шаблон с префиксом имени модели и суффиксом_detail.html, если не установлено иное.
context_object_name- переопределим имя Queryset по умолчанию.
context['title]- наш заголовок передаваемый в шаблон, аself.object.title- это наш объект, т.е наша статья, у которой мы получаем заголовок.
Нам необходимо добавить представление на основе классов PostDetailView в маршруты, добавим в файл urls.py приложения blog следующий код:
from django.urls import path
from .views import PostListView, PostDetailView
urlpatterns = [
path('', PostListView.as_view(), name='home'),
path('post/<str:slug>/', PostDetailView.as_view(), name='post_detail'),
]
В этом коде наше представление PostDetailView получает значение из параметра slug, передаваемого в URL-адресе.
Следующим шагом нам нужна функция, которая будет возвращать адрес у постов, для этого в модели Post добавим функцию:
from django.db import models
from django.core.validators import FileExtensionValidator
from django.contrib.auth.models import User
from django.urls import reverse
from mptt.models import MPTTModel, TreeForeignKey
class Post(models.Model):
"""
Модель постов для нашего блога
"""
# поля модели
class Meta:
db_table = 'blog_post'
ordering = ['-fixed', '-create']
indexes = [models.Index(fields=['-fixed', '-create', 'status'])]
verbose_name = 'Статья'
verbose_name_plural = 'Статьи'
def __str__(self):
return self.title
def get_absolute_url(self):
"""
Получаем прямую ссылку на статью
"""
return reverse('post_detail', kwargs={'slug': self.slug})
Данный метод позволяет получать прямую ссылку на статью, без вызова {% url '' %} Также мы импортировали reverse для формирования правильной ссылки.
Также изменим шаблон вывода списка статей, для этого откроем templates/blog/post_list.html и изменим код на следующий:
{% extends 'main.html' %}
{% block content %}
{% for post in posts %}
<img src={{ post.thumbnail.url }} alt={{ post.title }} width="150"/>
<strong><a href="{{ post.get_absolute_url }}">{{ post.title }}</a></strong>
<p>{{ post.description }}</p>
<small>{{ post.create }}</small>
<hr>
<span>{{ post.category.title }}</span>
{% endfor %}
{% endblock %}
Как мы видим, мы добавили ссылку на пост в имени записи. Теперь создадим шаблон полной статьи: templates/blog/post_detail.html:
{% extends 'main.html' %}
{% block content %}
<img src={{ post.thumbnail.url }} alt={{ post.title }} width="250"/>
<strong>{{ post.title }}</strong>
<p>{{ post.description }}</p>
<p>{{ post.text }}</p>
<small>{{ post.create }}</small>
<hr>
<span>{{ post.category.title }}</span>
{% endblock %}
При работе через класс DetailView нам не нужен цикл, поэтому напрямую обращаемся к post, это переменная, которую мы задали в context_object_name = 'post'.
Запустим сервер и перейдем на главную страницу проекта:
Мы видим что название нашей записи стало ссылкой, нажмем на нее:
Как мы видим, вывод записи работает прекрасно. В следующем шаге мы настроим наши slug, чтобы они создавались автоматически и могли обрабатывать кириллицу.