DetailView in Django: How to display a detail view

Video thumbnail

Once we know how to do file upload, we're going to create one of the fundamental CRUD processes in our Django application, which is to display a detail page (the Read or show); for this, we'll need to follow 3 steps:

  1. Create the function in the view, views.py of our application.
  2. Create the route at the application level.
  3. Create the template.

one of the first tasks I had to solve when using Django was displaying the complete information of a record: the typical "detail" within the CRUD flow. Initially, I did it with a Function-Based View (FBV), but I soon discovered that Django offers a more elegant and reusable way: the DetailView, and this is a fundamental difference we have in Django when compared to other frameworks like Laravel, which is that we have two ways to return views.

In this article, I'll teach you, from experience, how to switch from a manual view to a Class-Based View (CBV), what its advantages are, and how to customize it with real code.

What are class-based views in Django?

Video thumbnail

In Django, everything is an acronym: we have MTV (Model-Template-View), the ORM, and now, CBVs (Class-Based Views). For me, this is one of the three features I like most about the framework. It is fascinating to compare how each tool solves problems differently; for example, I love Laravel for how disruptive and innovative it is, but I also appreciate the traditional solidity of Django. Although Django doesn't change its core concepts very often, what it has works incredibly well, and CBVs are the best example.

The functions used up to this point in the views.py file at the level of each of the applications created so far are known as "Function-Based Views"; their name is quite obvious; it is a file full of functions that ultimately process a view based on the mapped route.

1. What exactly are CBVs?

So far we have worked with Function-Based Views (FBV). If you check our code, you will see functions that receive a request and return a response. However, we almost always end up doing the same thing: listing data or processing a form.

Whether the data comes from the client, the database, or an AI, the flow is the same: receive it, process it, and "spit" it back to the user. Just as in Django REST Framework we have "pre-cooked" ViewSets, in traditional Django we have CBVs to standardize these common operations.

Function-based views are quite flexible components in which we can execute anything supported by Django, since they are functions. But, in software development when wanting to create applications, there are always well-defined processes, such as rendering forms, handling forms, deleting, viewing record details, listings, etc. With this in mind, Django offers a simpler, cleaner, and more closed structure that can be used instead: "Class-Based Views" which, as the name indicates, now employ classes instead of functions, and with this, we have a more modular scheme to implement in views.

  • A view is a callable that takes a request and returns a response. This can be more than just a function, and Django provides examples of some classes that can be used as views.

  • Class-based views provide an alternative way to implement views as Python objects instead of functions. They do not replace function-based views, but they have certain differences and advantages compared to function-based views as we will see.

Class-based views allow us to define separate methods for each HTTP request method that we want to handle. Then, depending on the type of request received, Django takes care of calling the correct method in the class-based view; this is a huge advantage over function-based views, where we have to determine which operation is to be performed using many if-else conditions to handle each stage.

Class-based views, being classes, have methods and attributes that can be overwritten, and the main advantage is that, by using classes instead of functions, development becomes more structured and concise.

As a general recommendation, class-based views should be used whenever possible.

2. Advantages: Less Code, Better Readability

The main advantage of classes is reusability. While a function to process a form might take 20 lines of code, with a CBV you can achieve the same in 5 or 6 lines.

By modularizing, it is harder for the application to break and much easier to debug. Furthermore, we enter the territory of Object-Oriented Programming (OOP). Mastering classes and inheritance allows you to do incredible things.

Imagine a payment platform that accepts Stripe and PayPal. If you use functions, you would end up with a lot of if/else conditionals that are difficult to maintain. With classes, you can have a base class and use inheritance so that each payment gateway handles its particular logic independently.

3. CBV vs. FBV: When to use each?

In modern development, the ideal is to use both:

  • Use CBVs whenever possible to take advantage of the standard structure and code cleanliness.
  • Use FBVs (Functions) when you need to perform something very specific, manual, or complex that does not easily fit into the fixed structure of a class.

What is DetailView and what is it used for in Django?

DetailView is one of the generic Class-Based Views (CBV) included in Django.
Its purpose is simple: to display the detail of a single object from a model.

When I used the manual version, my function looked like this:

def show(request, pk): product = get_object_or_404(Product, id=pk) return render(request, 'show.html', {'product': product})

This function works perfectly, but over time I discovered that I could replace that entire block with just a few lines thanks to DetailView.

⚖️ CBV vs FBV: the natural evolution

Function-Based Views (FBV) are ideal for starting: total control, explicit logic, and clear learning.
However, as the project grows, maintaining each manual view becomes tedious.

DetailView, being a CBV, allows you to inherit common behaviors, reduce repeated code, and keep the architecture clean.

Key advantages of using DetailView

  • Shorter and easier-to-maintain code.
  • Automatic handling of 404 errors.
  • Pre-configured context and template.
  • Compatible with mixins and advanced customization.

Create the function in the view, views.py of our application

def show(request,pk):
   product = get_object_or_404(Product, id=pk)
   
   """try:
       product = Product.objects.get(pk=pk)
   except Product.DoesNotExist:
       #return HttpResponse(status=404)
       return HttpResponseNotFound()"""
   return render(request, 'show.html', { 'product' : product })

The function above simply searches for a record (Product in this case) given the pk; for this, we use the function called get_object_or_404, which searches for a record, and if it doesn't exist, it returns a 404 page; we can also do this manually:

try:
       product = Product.objects.get(pk=pk)
   except Product.DoesNotExist:
       #return HttpResponse(status=404)
       return HttpResponseNotFound()

Which searches for a record, and if it doesn't exist, it returns a 404 exception, but get_object_or_404 does all that for us.

Create the route at the application level

For the route, we create it with a parameter called pk of integer type and give it a name.

app_name="gestion"
urlpatterns = [
   path('',views.index),
   path('detail/<int:pk>',views.show, name="show"),
]

⚙️ How to create a DetailView in Django

Video thumbnail

To use DetailView, which is a Class-Based View instead of the function-based one we used before, you need to define a class in your views.py file:

from django.views.generic import DetailView
from .models import Product
class ProductDetailView(DetailView):
   model = Product
   template_name = 'show.html'
   context_object_name = 'product'

This class does the same as your show function, but automatically. Django searches for the object based on the pk or slug it receives from the URL.

Configure the route in urls.py

from django.urls import path
from .views import ProductDetailView
urlpatterns = [
   path('detail/<int:pk>/', ProductDetailView.as_view(), name='show'),
]

Here pk will be passed internally to the view, without you having to handle get_object_or_404.

Create the template

In our template, we display the content, the usual: the title, price, category, and description:

{% extends "base.html" %}
{% block title %}
   {{product.title}}
{% endblock title %}
{% block content %}
<h1>{{product.title}}</h1>
   <p>{{product.price}}$</p>
   <p>{{product.category.title}}</p>
   <p>{{product.description}}</p>
   
{% endblock %}

The product context comes ready thanks to context_object_name. If you don't define it, Django will use the lowercase model name by default.

The Risk of Errors 500

When we try to retrieve a record using a primary key (PK) that doesn't exist (Comment.objects.get(pk=50)), Django's ORM throws a DoesNotExist exception. A production application should never display a 500 debug error to the client (even if debug mode is disabled in settings.py), as this can expose internal information about the application's structure, as this may expose internal information about the application's structure.

To avoid this exception, the standard practice is to display a 404 error page and inform the user that they are trying to view the details of a record that doesn't exist. For example:

DoesNotExist at ***/20000

Comment matching query does not exist.

Para eso usamos el shortcut anterior en vez de una implementación como:

​​    try:
        comment = Comment.objects.get(pk=pk)
    except Comment.DoesNotExist:
        raise Http404
    if request.method == 'POST':

How DetailView works internally

When a request arrives, Django:

  • Gets the defined model (Product).
  • Searches for the object corresponding to the pk in the URL.
  • If it doesn't find it, it automatically raises a 404 error.
  • Renders the specified template with the context.

I used to do all this manually with get_object_or_404, but now Django handles the entire process.

⚙️ Automatic 404 error handling

If the object doesn't exist, DetailView raises Http404 without you having to wrap the logic in a try/except block.

This saves several lines of repeated code and helped me keep the views cleaner.

Customization and useful tricks

Sometimes you need to add extra data to the context (for example, related products or statistics).

You can do this by overriding the get_context_data() method:

class ProductDetailView(DetailView):
   model = Product
   template_name = 'show.html'
   context_object_name = 'product'
   def get_context_data(self, **kwargs):
       context = super().get_context_data(**kwargs)
       context['related'] = Product.objects.filter(category=self.object.category).exclude(pk=self.object.pk)
       return context

This allowed me to display similar products on the same page, without modifying the main template.

Integrating DetailView with the rest of the CRUD

DetailView is just one part of the set of generic views:

  • ListView → displays lists.
  • CreateView → creates records.
  • UpdateView → updates records.
  • DeleteView → deletes records.

Together they allow you to build a complete CRUD in minutes.

Complete Example of DetailView

# views.py
from django.views.generic import DetailView
from .models import Product
class ProductDetailView(DetailView):
   model = Product
   template_name = 'show.html'
   context_object_name = 'product'
# urls.py
from django.urls import path
from .views import ProductDetailView
urlpatterns = [
   path('product/<int:pk>/', ProductDetailView.as_view(), name='product_detail'),
]
<!-- show.html -->
<h1>{{ product.title }}</h1>
<p>{{ product.description }}</p>
<p>Precio: {{ product.price }}$</p>

And with that, you have a functional and clean detail view.

❓ Frequently Asked Questions (FAQ)

  • How to customize the context in a DetailView?
    • Override the get_context_data method and add your custom variables.
  • What to do if the object is not found?
    • Django handles this automatically by returning a 404 error.
  • Can I use DetailView with related models?
    • Yes, you can access the relationships from the object (self.object) or add additional queries to the context.

Conclusion

In my case, switching from a Function-Based View to DetailView was an immediate improvement: less code, fewer errors, and a more professional structure.

DetailView allows you to display a model's data with just a few lines, leveraging the full power of Django's generic view system.

If you're used to using functions like show() with get_object_or_404, this change will feel like a relief.

With this, we can generate a basic cycle to display the content of a record: in summary, we create a route to pass an ID, search for the record, and display it in a template.

The next step is static file handling.

We are going to generate a detail page or template to show the detail of a product in our application in Django, and you will learn how to create routes with parameters and the get_object_or_404 function.

I agree to receive announcements of interest about this Blog.

Andrés Cruz

ES En español