Django Class-based Views offers built-in functionality that handles backend logic.
There are different types of class-based views:
class PostListView(ListView):
Within the ListView, we’ll need to create a model called Post: model = Post
Add template for our homepage: template_name = 'blog/index.html'
To change the order of the post from newest to oldest, we need the - sign: ordering = ['-date']
import: from .views import PostListView
Replace homeview with PostListView:
path('', PostListView.as_view(), name='blog-home')
When we use a class-based view, it needs to be converted into an actual view, so we use a method called as_view()
.
class PostDetailView(DetailView):
model = Post
in urls.py
PostDetailView
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
In this step, we’ll create a template that will display our PostDetail.
Create a post_detail.html
in blog/templates
It will be similar to index.html, but all the post
will become object
, and we don’t need to loop over the article.
{% extends "blog/base.html" %} {% block content %}
<article class="media content-section">
<img
class="rounded-circle article-img"
src="{{ object.author.profile.image.url }}"
/>
<div class="media-body">
<div class="article-metadata">
<a class="mr-2" href="#">{{ object.author }}</a>
<small class="text-muted">{{ object.date|date:"F d, Y" }}</small>
<!-- Add button for Update and Delete-->
{% if object.author == user %}
<div>
<a
class="btn btn-secondary btn-sm mt-1 mb-1"
href="{% url 'post-update' object.id %}"
>Update</a
>
<a
class="btn btn-danger btn-sm mt-1 mb-1"
href="{% url 'post-delete' object.id %}"
>Delete</a
>
</div>
{% endif %}
</div>
<h2 class="article-title">{{ object.title }}</h2>
<p class="article-content">{{ object.content }}</p>
</div>
</article>
{% endblock content %}
Right now, they are just dead links. So we need to update the link to the post title.
<h2>
<a class="article-title" href="{% url 'post-detail' post.id%}"
>{{ post.title }}</a
>
</h2>
path('post/new/', PostCreateView.as_view(), name='post-create'),
create post post_form.html template
It’s quite similar to register.html template
{% extends "blog/base.html" %}
{% load crispy_forms_tags %}
{% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Blog Post</legend>
{{ form|crispy }}
</fieldset>
<div class="form-group">
<button class="btn btn-outline-info" type="submit">Post</button>
</div>
</form>
</div>
{% endblock content %}
Tell Django how to find the URL of a model object is to create a get absolute URL method in our model that returns that path.
Add this blog/models.py
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
It becomes:
from django.db import models
from django.utils import timezone
from django.contrib.auth.models import User
from django.urls import reverse
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
date = models.DateTimeField(default=timezone.now)
author = models.ForeignKey(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('post-detail', kwargs={'pk': self.pk})
<a class="nav-item nav-link" href="{% url 'post-create' %}">New Post</a>
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
in urls.py
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
In views.py
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/' # when post is deleted, redirect user to Homepage
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
in urls.py
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
Create delete form template: post_confirm_delete.html
.
{% extends "blog/base.html" %} {% block content %}
<div class="content-section">
<form method="POST">
{% csrf_token %}
<fieldset class="form-group">
<legend class="border-bottom mb-4">Delete Post</legend>
<h2>Are you sure you want to delete the post "{{ object.title }}"</h2>
</fieldset>
<div class="form-group">
<button class="btn btn-outline-danger" type="submit">Yes, Delete</button>
<a
class="btn btn-outline-secondary"
href="{% url 'post-detail' object.id %}"
>Cancel</a
>
</div>
</form>
</div>
{% endblock content %}
Overall, here is the final version of our urls.py.
from django.urls import path
from .views import (
PostListView,
PostDetailView,
PostCreateView,
PostUpdateView,
PostDeleteView
)
from . import views
urlpatterns = [
path('', PostListView.as_view(), name='blog-home'),
path('post/<int:pk>/', PostDetailView.as_view(), name='post-detail'),
path('post/new/', PostCreateView.as_view(), name='post-create'),
path('post/<int:pk>/update/', PostUpdateView.as_view(), name='post-update'),
path('post/<int:pk>/delete/', PostDeleteView.as_view(), name='post-delete'),
path('about/', views.about, name='blog-about'),
]
and views.py
from django.shortcuts import render
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
from django.views.generic import (
ListView,
DetailView,
CreateView,
UpdateView,
DeleteView
)
from .models import Post
def home(request):
context = {
'posts': Post.objects.all()
}
return render(request, 'blog/home.html', context)
class PostListView(ListView):
model = Post
template_name = 'blog/home.html' # <app>/<model>_<viewtype>.html
context_object_name = 'posts'
ordering = ['-date_posted']
class PostDetailView(DetailView):
model = Post
class PostCreateView(LoginRequiredMixin, CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
class PostUpdateView(LoginRequiredMixin, UserPassesTestMixin, UpdateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
class PostDeleteView(LoginRequiredMixin, UserPassesTestMixin, DeleteView):
model = Post
success_url = '/'
def test_func(self):
post = self.get_object()
if self.request.user == post.author:
return True
return False
def about(request):
return render(request, 'blog/about.html', {'title': 'About'})