Convert a Django function-based-view into a class-based-view (DetailView)
DoriDoro
Posted on September 25, 2024
Introduction
To convert this function-based view (FBV) into a class-based view (CBV), I will use the Django's DetailView
, which is specifically designed to display a single object from the database.
This is the code of the Post model which is used for both views:
# models.py
from django.db import models
from django.utils import timezone
class Post(models.Model):
title = models.CharField(max_length=250)
slug = models.SlugField(max_length=250, unique_for_date="publish")
author = models.ForeignKey(
"account.User", on_delete=models.CASCADE, related_name="blog_posts"
)
body = models.TextField()
publish = models.DateTimeField(default=timezone.now)
def __str__(self):
return self.title
Here's the function-based view I'm creating for a single post view. In the URL, the day/month/year/slug
is displayed, such as blog/1/1/2024/test-blog/
. With this way of displaying the URL, we provide the search engines with SEO-friendly URL's.
Function-Based view (FBV):
# views.py
from django.shortcuts import render, get_object_or_404
from .models import Post
def post_detail(request, day, month, year, post):
post = get_object_or_404(
Post,
status=Post.Status.PUBLISHED,
publish__day=day,
publish__month=month,
publish__year=year,
slug=post,
)
return render(request, "post/detail.html", {"post": post})
Here you can see the pattern of the URL.
# urls.py
from django.urls import path
from blog import views
urlpatterns = [
path(
"<int:day>/<int:month>/<int:year>/<slug:post>/",
views.post_detail,
name="post_detail",
),
]
This is the html template of the list of all posts which will lead to a single detailed post.
# html template for listing all posts
{% extends "base.html" %}
{% block content %}
<h1>My Blog</h1>
{% for post in posts %}
<h2>
<a href="{% url 'blog:post_detail' day=post.publish.day month=post.publish.month year=post.publish.year post=post.slug %}">
{{ post.title }}
</a>
</h2>
<p class="date">
Published {{ post.publish }} by {{ post.author }}
</p>
{{ post.body|truncatewords:30|linebreaks }}
{% endfor %}
{% endblock %}
When you click on the link of the {{ post.title }}
and been redirected to a detailed view of one post, the URL will be like: blog/1/1/2024/test-blog/
(blog/day/month/year/slug-of-the-post/
)
Now, I will show you how the function-based view has been converted to a class-based view:
Class-Based View (CBV) Conversion:
# views.py
from django.views.generic.detail import DetailView
from django.shortcuts import get_object_or_404
from .models import Post
class PostDetailView(DetailView):
model = Post
template_name = "post/detail.html"
context_object_name = "post"
def get_object(self):
return get_object_or_404(
Post,
status=Post.Status.PUBLISHED,
publish__day=self.kwargs['day'],
publish__month=self.kwargs['month'],
publish__year=self.kwargs['year'],
slug=self.kwargs['post']
)
Breakdown:
-
DetailView
: Handles the logic of displaying a single object. -
model
: Defines the model (i.e.,Post
) to be used in the view. -
template_name
: Specifies the template file to be used ("post/detail.html"
). -
context_object_name
: Sets the context variable name for the object passed to the template ("post"
). -
get_object
: Customizes how the object is retrieved using the same logic from your FBV withget_object_or_404
.
URL Pattern:
The URL pattern for this view, will be the same like before and you have to pass the day
, month
, year
, and post
parameters to the view:
from django.urls import path
from .views import PostDetailView
urlpatterns = [
path('<int:year>/<int:month>/<int:day>/<slug:post>/', PostDetailView.as_view(), name='post_detail'),
]
This approach keeps the view clean and leverages Django's class-based view system for more reusability and structure.
Posted on September 25, 2024
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.