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.
 

 
    
Top comments (0)