Join my Laravel for REST API's course on Udemy 👀

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

April 23, 2021  ‐ 2 min read

There is great magic involved in Django its class-based views, the good kind of magic. The kind of magic that saves me lots of time and lines of code. However, using them requires you to follow some conventions.

Such a conventions in Django is using properly named url variables when using detail views. View classes like DetailView, DeleteView, UpdateView rely on the presence of either a url variable with the name pk or slug. These classes inherit a method called get_object from the SingleObjectMixin class. This get_object method is used to find the right model instance to be displayed in the view.

So lets consider the following example: a detail view for a blog post model.

# blog/views.py
from django.views import generic
from blog.models import Post


class DetailView(generic.DetailView):
    model = Post
    template_name = 'blog/detail.html'

In order to comply with the requirements from the get_object method we should add a url containing a variable named either pk or slug. Otherwise we would get an AttributeError from Django with the message: Generic detail view DetailView must be called with either an object pk or a slug in the URLconf.

When using the path() function you should keep the following format in mind when adding url variables: <variable type:variable name>.

If you prefer to use the primary key for you DetailView you should add it in the urlpatterns like:

# urls.py
from django.urls import path
from blog import views

urlpatterns = [
    path('<int:pk>/', views.DetailView.as_view()),
]

If you prefer to use a slug for your DetailView you should first make sure your model implements a SlugField. Then add the slug as <slug:slug>, bit confusing maybe but both the variable type and name are supposed to be named slug:

# urls.py
from django.urls import path
from blog import views

urlpatterns = [
    path('<slug:slug>/', views.DetailView.as_view()),
]