Django AttributeError: Generic detail view xxx must be called with either an object pk or a slug in the URLconf

created at 07-16-2021 views: 1

Introduction

Using DetailView, prompt error:

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

reason

To use DetailView, the primary key id or slug field must appear in the url URL

DetailView case

myapp/views.py:

from django.utils import timezone
from django.views.generic.detail import DetailView
from articles.models import Article

class ArticleDetailView(DetailView):

    model = Article

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

myapp/urls.py

from django.urls import path
from article.views import ArticleDetailView

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

    ]

myapp/article_detail.html

<h1>{{ object.headline }}</h1>
<p>{{ object.content }}</p>
<p>Reporter: {{ object.reporter }}</p>
<p>Published: {{ object.pub_date|date }}</p>
<p>Date: {{ now|date }}</p>

If you insist that pk and slug do not appear in the url, the solution:

myapp/urls.py

from django.urls import path
from article.views import ArticleDetailView

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

    ]

myapp/views.py

from django.utils import timezone
from django.views.generic.detail import DetailView
from articles.models import Article

class ArticleDetailView(DetailView):

    model = Article

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

    def get_object(self, queryset=None):
        if queryset is None:
            queryset = self.get_queryset()
        name = self.kwargs.get('title') or self.request.GET.get('title') or None
        queryset = queryset.filter(title=title)
        obj = queryset.get()
        return obj

Mainly to rewrite the get_object method, check the source code and you can find that pk and slug judgments appear in get_object

def get_object(self, queryset=None):
    """
    Return the object the view is displaying.

    Require `self.queryset` and a `pk` or `slug` argument in the URLconf.
    Subclasses can override this to return any object.
    """
    # Use a custom queryset if provided; this is required for subclasses
    # like DateDetailView
    if queryset is None:
        queryset = self.get_queryset()

    # Next, try looking up by primary key.
    pk = self.kwargs.get(self.pk_url_kwarg)
    slug = self.kwargs.get(self.slug_url_kwarg)
    if pk is not None:
        queryset = queryset.filter(pk=pk)

    # Next, try looking up by slug.
    if slug is not None and (pk is None or self.query_pk_and_slug):
        slug_field = self.get_slug_field()
        queryset = queryset.filter(**{slug_field: slug})

    # If none of those are defined, it's an error.
    if pk is None and slug is None:
        raise AttributeError(
            "Generic detail view %s must be called with either an object "
            "pk or a slug in the URLconf." % self.__class__.__name__
        )

    try:
        # Get the single item from the filtered queryset
        obj = queryset.get()
    except queryset.model.DoesNotExist:
        raise Http404(_("No %(verbose_name)s found matching the query") %
                      {'verbose_name': queryset.model._meta.verbose_name})
    return obj
Please log in to leave a comment.