Part-14

Implement CRUD in Our Django App

Atufa Shireen
The Startup
Published in
4 min readSep 7, 2020

--

Moving back to our blogapp, we had defined some views as follows

def home(request):
content={'posts':Post.objects.all()}
return render(request, 'blogapp/home.html',content)

def post(request):
return HttpResponse('<h1>A detailed post..</h1>')
def create(request):
return HttpResponse('<h1>Create a new post</h1>')
def update(request):
return HttpResponse('<h1>Update your post..</h1>')

def delete(request):
return HttpResponse('<h1>Delete a post..</h1>')

There are many more things to do here.., implement update, delete ,create new or view detailed post.Let’s use class based views here

Class based views have many built-in functionalities,let’s use them to allow users to create,read,update or delete a post using CreateView, DetailView or ListView, UpdateView and DeleteView

Import these views in the blogapp/views.py as follows,

from django.views.generic import CreateView, ListView, UpdateView, DeleteView, DetailView 

DetailView

We want to show detailed view of any post in their respective pages or at </post/primary_key> and to do that let’s replace the post function with this

class PostDetailView(DetailView):
model = Post # blogapp/post_detail.html
template_name='blogapp/details.html'
context_object_name = 'post'

create your details.html and at the urlpattern, import the class from .views and add

from .view import PostDetailView
path("post/<int:pk>", PostDetailView.as_view(), name="post-detail"),

remember to call .as_view() method of the class and the <int:pk> passes the primary key of the post to the url

The DetailView requires the model it should work on the default template name is as follows

<app_name>/<model>_<viewtype>.html

or in this case it will be

blogapp/post_detail.html

the default context_object_name will be object so renaming our details.html to post_detail.html and using the default context_object_name we can shrinkour class as follows

class PostDetailView(DetailView):
model = Post

and in post_details.html

{% load humanize %}
<img src="{{post.bg_pic.url}}">
<div>
<h3>{{post.title}}</h3>
<p>{{post.content}}</p>
<div class="date">Published {{post.date_posted|naturaltime}}</div>
<div class="tags">
<div class="tag">By {{post.author}}</div>
</div>
</div>

ListView

Let’s replace our home function

we’ve been passing Post.objects.all to our page but setting model to the Post will do this job for us , and to order the posts according to the date we pass the ordering attribute {the -at the beginning arranges from the recent to the last one}

class HomeView(ListView):
model = Post
context_object_name = 'posts'
template_name = 'blogapp/home.html'
ordering = ['-date_posted']

Just decided to leave the name of the index page as home.html and used posts to keep the things simple here,the urlpattern for this will be

path('', PostListView.as_view(), name=’blog-home’),

CreateView

Let’s replace our create function

class PostCreateView(CreateView):
model = Post
fields = ['title','content','bg_pic']

here we specify the fields which we want our users to add.The default template name for this view will be different as post_form.html.Now import this in the urls.py and

path("post/create/new",PostCreateView.as_view(),name='create'),

Do not forget to add the enctype in the form to set bg_pic

When you test this view you might notice that when you try to submit it yells at you with

an Integrity error{not null constraint failed}

well that’s because we are trying to create a post without an author.Let’s override this validation form and add the author before submission

def form_valid(self, form):
form.instance.author=self.request.user
return super().form_valid(form)

we assigned the current user as the author to the form and called the Parent class form_valid method again with the new form,and test again..,

what’s this error now

The ImproperlyConfigured error says that it successfully created the post but doesn’t where to redirect now, and also gives a hint to either redirect to a url or create a get_absolute_url ,let’s use the former one.In Models.py

from django.urls import reversedef __str__(self):
...
def get_absolute_url(self):
return reverse('post-detail',kwargs={'pk':self.pk})

Redirect Method will redirect you to a specific route in General. Reverse Method will return the complete URL to that route as a String

Obviously we want an authenticated user to create a post we used @login_required decorator in function view but in class based views we’ll use loginmixin, In views.py ,import this and pass it to our class

from django.contrib.auth.mixins import LoginRequiredMixinclass PostCreateView(LoginRequiredMixin,CreateView):
.....

when someone try creating post as an anonymous user, and they will be redirected to the login url

UpdateView

For updating the post we’ll keep our class functionality same and just change the name of our class, keep the same html file for the view ,import it in the urls and give it a name and we’re good to go.

seriously

Not really ,we want only the authors of the post to be able to update or delete a post and not any random logged in user ,let’s use another mixin for that and pass it to update view class and define(rewrite) test_func function

PostUpdateView(LoginRequiredMixin,UserPassesTestMixin,UpdateView):
....
def test_func(self):
post=self.get_object()
return self.request.user==post.author

and that’s it

DeleteView

Add the UserPassesTest and LoginRequired mixin to this view

class PostDeleteView(LoginRequiredMixin,UserPassesTestMixin,DeleteView):
model = Post
success_url = '/'
def get_test_func(self):
post=self.get_object()
return self.request.user==post.author

the default template name for this will be post_confirm_delete.html

{% extends "blogapp/base.html" %}
{% block body %}

<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>

{% endblock body %}

Now in base.html, we can add new post link under if user.is_authenticated and under detailed page(post_detail), add the update or delete functionality something like this..

<div>
{%if object.author==user%}
<a href="{%url 'update' object.id%}">Update</a>
<a href="{%url 'delete object.id'%}">Delete</a>
</div>

--

--