Создание сайта на Django: Урок 13, детальная страница статьи (DetailView)
avatar
7 | (offline)
❤️‍🔥Notehunter Developer
Добавлено:
Категория: Руководства «Django»
Комментариев: 6

Что же такое представление DetailView?

Это интересное и универсальное представление для одиночного объекта qs, получаемое по slug или же pk (primary key).

Давайте попробуем создать такое представление и получить определенную статью. Не забываем импортировать DetailView!

modules/blog/views/articles.py

from django.views.generic import ListView, DetailView

class ArticleDetailView(DetailView):
    model = Article
    template_name = 'modules/blog/articles/article-detail.html'
    context_object_name = 'article'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        context['title'] = self.object.title
        return context

Пояснения:

  • model - наша модель статьи
  • template_name - название нашего кастомного шаблона
  • context_object_name - представленная переменная в шаблоне
  • context['title] - наш заголовок передаваемый в <title></title>
  • self.object.title - это наш объект, т.е наша статья, у которой мы получаем заголовок.

Давайте добавим ссылку в модель, а после в сам список статей.

modules/blog/models/articles.py (не забываем импорт from django.urls import reverse)

from django.urls import reverse

class Article(models.Model):
    """
    Модель статей проекта. 
    """
    title = models.CharField(verbose_name=_('Заголовок'), max_length=255)
    #some fields
    #some methods
    
    def get_absolute_url(self):
        """
        Ссылка на статью
        """
        return reverse('article-detail', kwargs={'slug': self.slug})

Пояснения:

  • Данный метод позволяет получать прямую ссылку на статью, без вызова {% url '' %}

Далее идем редактировать urls.py

modules/blog/urls.py (импортируем from modules.blog.views import ArticleDetailView)

from django.urls import path

from modules.blog.views import ArticleListView, ArticleDetailView

urlpatterns = [
    path('', ArticleListView.as_view(), name='article-list'),
    path('<str:slug>/', ArticleDetailView.as_view(), name='article-detail'),
]

Пояснения:

  • Как видите, мы добавили наше созданное представление, задали ему работу от <slug>, дали ему название article-detail.

А теперь идем в шаблон списка статей, и обновляем, добавляя метод get_absolute_url:

templates/modules/blog/articles/article-list.html

{% extends 'main.html' %}

{% block content %}
    {% for article in articles %}
        <figure>
            <img src="{{ article.get_thumbnail }}" width="200" alt="{{ article.title }}">
        </figure>
        <strong>{{ article.title }}</strong>
        <p>{{ article.short_description }}</p>
        <a href="{{ article.get_absolute_url }}">Перейти к полной статье</a>
    {% endfor %}
{% endblock %}

Пояснения:

  • Мы добавили наш метод, и на скриншоте ниже мы можем видеть путь к прочтению детальной статьи

Далее, давайте создадим article-detail.html в том же месте templates/modules/blog/articles/ со следующим содержимым:

templates/modules/blog/articles/article-detail.html

{% extends 'main.html' %}

{% block content %}
    <div class="card border-0">
        <div class="card-body">
            <div class="row">
                <div class="col-md-4">
                    <figure>
                        <img src="{{ article.get_thumbnail }}" width="200" alt="{{ article.title }}">
                    </figure>
                </div>
                <div class="col-md-8">
                     <h5 class="card-title">
                        {{ article.title }}
                    </h5>
                    <small class="card-subtitle">
                        {{ article.created_at }} / {{ article.category }}
                    </small>
                    <div class="card-text">
                        {{ article.full_description }}
                    </div>
                </div>
            </div>
        </div>
    </div>
{% endblock %}

Пояснения:

  • Как видите, цикл for нам уже не нужен в шаблоне для детальной страницы. Ведь у нас один объект получаемый по slug. 
  • Я использовал разметку bootstrap.
  • Также, в нужную разметку я добавил поля нашего объекта. 

Выглядит это следующим образом:

Также в DetailView есть много других интересных методов, как пользовательский slug_field, extra_context который отвечает за дополнительные вызовы других моделей в детальную страницу. Подробнее вы можете прочесть на официальном сайте Django.

Комментарии к статье 6
  • veravel
    18 октября 2022 г. 20:40

    Добрый день, у меня проблема которую я не могу понять как решить, после добавления класса в modules/models/articles.py у меня выдает ошибку: NameError: name 'AbstractBaseMeta' is not defined Подскажите что я пропустил...

    • Razilator
      18 октября 2022 г. 21:59

      veravel, здравствуйте, в одном из уроков я создавал абстрактную модель с полями для SEO. Эту модель я создал в приложении System. Урок 18!. Вместо AbstractBaseMeta наследуйтесь в Article от models.Model, а именно:

      class Article(models.Model):
      

      Скорее всего, я нечаянно вставил AbstractBaseMeta из своего кода, прежде чем написать урок. Поэтому это моя вина и я исправлю статью. Если Вы следуете шаг за шагом, в 18 уроке придете к этой конструкции с AbstractBaseMeta, и поймете наследование классов и моделей.

      Спасибо, что сообщили о проблеме, надеюсь у Вас все получается :)

  • slug
    8 ноября 2022 г. 23:31

    Привет! Я в моделях использую название URL вместо Slug. И вот когда дошел до детальной страницы, то столкнулся вот с такой проблемой.

    Generic detail view ArticleDetailView must be called with either an object pk or a slug in the URLconf.

    Не понимаю что я упустил?

    • Razilator
      9 ноября 2022 г. 6:48

      slug, доброе утро. Тогда добавьте в контроллер ArticleDetailView атрибут slug_field = 'URL'. Выглядеть это должно вот так:

      class ArticleDetailView(DetailView):
          """
          Контроллер детальной статьи
          """
          model = Article
          template_name = 'modules/blog/articles/articles-detail.html'
          context_object_name = 'article'
          slug_field = 'URL'
      

      Это должно помочь в решении вашей задачи. Также необходимо в urls.py вместо <str:slug>, писать <str:url>

      • slug
        9 ноября 2022 г. 14:25

        Razilator, Вроде так и делал. Но не получилось. Я в итоге переименовал все как у вас и все заработало)

        • Razilator
          9 ноября 2022 г. 14:52

          slug, хорошо, но если что, метод описанный выше мной, должен был сработать. Но раз все работает по новому, то я рад, что все получилось)

Форма добавления комментария (необходима регистрация)