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)