A Django Blog In VS Code — Class-Based-View & CRUD

How To Create A Blog in VS Code — Part VIII — DjangoSeries Episode # 11

J3
Jungletronics
9 min readFeb 13, 2022

--

This is all about django class_based view & CRUD.

INDEXES:

Create
Read(List)
Update
Delete

Welcome!

We should use class_based views (CBVs) in our applications.

It’s better to stick with the Django convention unless you have a very good reason not to.

CBVs are generic views that provide abstract classes implementing common web development tasks built by professionals and covering all common behaviors.

They have an amazing structured API, and you can use all of the advantages of object-oriented programming when you use CBVs.

It makes your source code more clear and readable.

Forget the pain of using Django standard view functions for listings, CRUD operations, forms processing, etc

This post will deal with it: CBVs.

More details on this view class at Classy Class-Based Views by Refresh Oxford. 👏👏👏.

Let’s get started!

Django Naming Convention For Views(V) Models(M) Templates(T)

As Django is MVT, first thing first:

This is the my name_convention_table used hereafter:

This table sticks with the Django convention nicely:) We suggest that you stick to it as it will work just fine with Django. (_underlines_ is my pattern, no more dashes!) — (*) Django template forms ends with model_form.html → Step#09; (**) Django will redirect the deletion to model_confirm_delete.html → Step#18.

00#Step — This is a continuation of this post.

If we have only used function_based generic views in our Django project so far and find out that they are limited, we should think about class_based view instead. Let’s get it on!

READ — LIST — : Modus operandi V(iew) U(rls) T(emplate)

01#Step — V(iew): GoTo blog\views.py, and add these lines:

...
from django.views.generic import ListView
# def home(request):
# context = {
# 'posts': Post.objects.all()
# }
# return render(request, 'blog/home.html', context)


class PostListView(ListView):
model = Post

# context_object_name = 'object_list'
ordering = ['-date_posted']
...
The No Code revolution is here! Just one line: model = Post → No context, no request, no render method…Just Amazing!

02#Step —U(rls): GoTo blog\urls.py, and add these lines:

from django.urls import path
from blog import views
from blog.views import PostListView
urlpatterns = [
# path('', views.home, name='blog-home'),
path('', PostListView.as_view(), name='post_list'),
path('about/', views.about, name='about_page'),
]

Just be aware of these points:

1- Comment out the old home function-based-view (def home());

2- Let’s say that we want to use this postListView class instead of our current home function as our home by importing ListView CBV;

3- How Django knows about the old posts as we pass no context?

The generic view will query the database to get all records for the specified model (Post) then render a template located at /djang_project/blog/templates/blog/post_list.html (which we will created below).

Within the template you can access the list of posts with the template variable named object_list OR post_list (i.e. generically "the_model_name_list").

4- We are showing our newest blog-post first by just adding this line:

ordering = ['-date_posted'] — the minus sign do the trick!

03#Step — T(emplate): Create blog\post_list.html, and add these lines:

Just be aware of these points:

1- If you compare to the old home.html you’ll see that now we are looping throught object_list instead of posts;

All of Django’s database backends automatically convert strings into the appropriate encoding for talking to the database. They also automatically convert strings retrieved from the database into strings. You don’t even need to tell Django what encoding your database uses: that is handled transparently. (from https://docs.djangoproject.com/en/4.0/ref/unicode/)

2- If you compare to the commented code, you’ll find fewer lines of code. That’s the premium to stick with Django convention!

Clean and valid code means less confusing bugs to fix, easier handover to other developers and better code security.

Django is a free and open source Python web framework that helpfully solves common development challenges and allows you build flexible, well-structured applications. (from https://www.toptal.com/django/django-top-10-mistakes)

3- The post are now ordered in reverse order, as usual in the industry. Django’s generic views really shine when it comes to presenting views of your database content. Because it’s such a common task, Django comes with a handful of built-in generic views to help generate list and detail views of objects.

DETAIL — : Modus operandi V(iew) U(rls) T(emplate)

04#Step — V(iew): GoTo blog\views.py, and add these lines:

...
from django.views.generic import ... DetailView
...
class PostDetailView(DetailView):
model = Post
...

05#Step — U(rls): GoTo blog\urls.py, and add these lines:

...
from blog.views import ... PostDetailView
...
urlpatterns = [
...
path('post/<int:pk>/', PostDetailView.as_view(),
name='post_detail'),

...
]

06#Step — T(emplate): Create blog\post_detail.html, and add these lines:

Just be aware of these points:

1- There is no loop here;

2- object are used instead of post; While this view is executing, self.object will contain the object that the view is operating upon. [Django doc link]

3- Each post now have it’s own page! This is the detail Django template’s rule. Fine! Now let’s create a post…

CREATE — : Modus operandi V(iew) U(rls) T(emplate)

07#Step — V(iew): GoTo blog\views.py, and add these lines:

...
from django.views.generic import ... CreateView
from django.contrib.auth.mixins import LoginRequiredMixin
...class PostCreateView(CreateView):
model = Post
fields = ['title', 'content']
def form_valid(self, form):
form.instance.author = self.request.user
return super().form_valid(form)
...

08#Step — U(rls): GoTo blog\urls.py, and add these lines:

...
from blog.views import ... PostCreateView
urlpatterns = [
...
path('post/new', PostCreateView.as_view(), name='post_create'),
...
]

09#Step — T(emplate): Create blog\post_form.html, and add these lines:

Just be aware of these points:

1- You would expect post_create but Django’s convention is post_form;

2- The template just prints the form; by working in Django we come across patterns at a higher level of abstraction. Well, that’s marvelous, indeed!

10#Step — Modify blog\models.html to:

...
from django.urls import reverse
...def __str__(self):
return self.title
def get_absolute_url(self):
return reverse("post_detail", kwargs={"pk": self.pk})

11#Step — Distinguish which link-button clicked in django:

<li class="nav-item">
<a class="nav-link" href="{% url 'post_create' %}">New Post</a></li>
Modify the previous django_project\blog\templates\blog\base.html (our new home page) by including new list item (New Post) — green — and change to post_list the routes — orange — .
{% 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 %}
Modify django_project\blog\templates\blog\post_detail.html created in step#03 by including the condition statement below:

12#Step — As we can’t use decorators with class lets use mixins :

That’s basically just a class that we inherit from that will add that login functionality to the view so let’s go ahead and add this in:

Modify blog\views.py file to:

...
CreateViewfrom django.contrib.auth.mixins import LoginRequiredMixin
...
class PostCreateView(LoginRequiredMixin, CreateView):
....

UPDATE — : Modus operandi V(iew) U(rls) T(emplate)

13#Step — V(iew): GoTo blog\views.py, and add these lines:

...
from django.views.generic import ... UpdateView
from django.contrib.auth.mixins import LoginRequiredMixin, UserPassesTestMixin
...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
...

14#Step — U(rls): GoTo blog\urls.py, and add these lines:

...
from blog.views import ...,PostUpdateView
urlpatterns = [
...
path('post/<int:pk>/update/', PostUpdateView.as_view(),
name='post_update'),
...
]

15#Step — T(emplate): For the template it’s just going to use that same post form template that we created for the create view at step#09, so we don’t even need to add another template. Let’s go on…

Now another operation:

DELETE — : Modus operandi V(iew) U(rls) T(emplate)

16#Step — V(iew): GoTo blog\views.py, and add these lines:

17#Step — U(rls): GoTo blog\urls.py, and add these lines:

...
from blog.views import ..., PostDeleteView
urlpatterns = [
...
path('post/<int:pk>/delete/', PostDeleteView.as_view(),
name='post_delete'),
...
]

18#Step — T(emplate): Create blog\post_confirm_delete.html, and add these lines:

Ups, we almost forget….GoTo django_project/settings.py and change theses lines:

# Redirecting to homeLOGIN_REDIRECT_URL = 'post_list'
ACCOUNT_LOGOUT_REDIRECT_URL = 'post_list'
LOGIN_URL = 'login'

The final step is:

19#Step — run the server:

python manage.py runserver

20#Step — login as userTest4 and try to post:

http://127.0.0.1:8000/post/4/
When you post a brand new Post object you will be redirected to the detail of that post. The get_absolute_url(self) will be called and reverse returns an string that points to detail page (passing the pk). That’s why you update blog\models.html at step#10.

21#Step — Let’s try /post/new/ even logged-off:

As you are logged-off, if you try to directly /post/new/ you will be redirect to /login/ page and once you logged on the /?next=/post/new/ will run. That’s what we want! See the config line LOGIN_URL = ‘login’ in django_project/settings.py and the mixins at the view step#13.

Now, log in as userTest4 and Post a new blog by clicking New Post:

And there you have it! Everything is working fine! Update & Delete is kicking & running!

That’s all Folks!

I would like to remind everyone, especially those Python’s lovers, soon we will be back with Posts Pagination & Quick DB Population.

See you around. Bye!

👉 git

References & Credits

Python Django Tutorial: Full-Featured Web App by Corey Schafer

Django Documentations — Unicode data — by djangoproject.com

Top 10 Mistakes that Django Developers Make by toptal.com

Requeriments.txt: pip freeze creates some weird path instead of the package version by stackoverflow.com

How to Create User Sign Up View by Vitor Freitas

👌️Classy Class-Based Views — Detailed descriptions, with full methods and attributes, for each of Django’s class-based generic views by Refresh Oxford

Getting started with the web by Mozilla Foundation

🙀️Wireflow — free, online and open source tool for creating beautiful user flow prototypes by https://wireflow.co/

🙀️excalidraw.com by excalidraw is free, online and open source tool to draw :)

🙀️Django Project Blueprint — Awesome Free Tutorial — Check it out! — learn by building thing with Matthew Segal

Classy Class-Based Views — Detailed descriptions, with full methods and attributes, for each of Django’s class-based generic views.

👌️Official Django Documentations — All authentication views — This is a list with all the views django.contrib.auth provides by https://docs.djangoproject.com/

Related Posts

00#Episode — DjangoSerie — Django Intro — How To Build your First App in Python Django Framework — DjangoSeries

01#Episode — DjangoSerie — Django MTV In VS Code — How To Install Django Inside Virtual VS Code

02#Episode — DjangoSerie — Can You Solve This in Python? — Here is A Basic Python Question!

03#Episode — DjangoSerie — JUNGLE-DJANGO Webpage! This Is My New Django Netflix Clone Page!

04#Episode — DjangoSerie — A Django Blog In VS Code — Quick Start!Part_I

05#Episode — DjangoSerie — A Django Blog In VS Code — Database, Migrations & Queries — Part_II

06#Episode — DjangoSerie — A Django Blog In VS Code — Bootstrap, Tailwind CSS — Part_III

07#Episode — DjangoSerie — A Django Blog In VS Code — Forms & Validations — Part_IV

08#Episode — DjangoSerie — A Django Blog In VS Code — Login & Logout — Part_V

09#Episode — DjangoSerie — A Django Blog In VS Code — Upload Profile Picture — Part_VI

10#Episode — DjangoSerie — A Django Blog In VS Code — Update & Resize Picture — Part_VII

11#Episode — DjangoSerie — A Django Blog In VS Code — Class-Based-View & CRUD — Part_VIII (this one :)

12#Episode — DjangoSerie — A Django Blog In VS Code — Posts Pagination & Quick DB Population — Part_IX

13#Episode — DjangoSerie — A Django Blog In VS Code — Self-Service Django Password Reset — Part_X

14#Episode — DjangoSerie — A Django Blog In VS Code — Heroku Deploy — How To Push Your Site To Productio — Part_XI (this last one…whew!)

HowTo Run this Tutorial - From the Scratch - In your Machine:)Annotations: Quick Start - video #TakeTheFirstStepToLearnDjango0 - Download DJG_10/Django_project from my git repo;
1 - On your desktop, create a dir and named it as Tutorial_11;
2 - Inside this paste the previous Django_project file;
3 - GoTo vscode Terminal (command prompt) and ...
4 - Run this quick_script
(copy/paste inside you terminal and hit enter and wait until
Pillow is up and running...):
python -m venv djangoEnv
djangoEnv\Scripts\activate
python -m pip install --upgrade pip
python -m pip install django
python -m django --version
pip install django-crispy-forms
pip install Pillow
5- GoTo Step#01 of this tutorial.And you are good to go!

Sharing is caring.

by me

GIF Comparison with previous implementation

“Once we believe in ourselves, we can risk curiosity, wonder, spontaneous delight, or any experience that reveals the human spirit.”

-E. Cummings.

--

--

J3
Jungletronics

😎 Gilberto Oliveira Jr | 🖥️ Computer Engineer | 🐍 Python | 🧩 C | 💎 Rails | 🤖 AI & IoT | ✍️