Part-14
Implement CRUD in Our Django App
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..,
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.
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>