In this tutorial I will take a simple notes app built with function based views (FBV) and convert them into class based views (CBV).
This post will be used as a guide for a YouTube tutorial so I recommend watching the full video tutorial and referencing the source code.
Our Function Based Views
Let's start by taking a quick look at the views we currently have.
Our views file has views to follow the basic CRUD operations for Creating, Reading, Updating and Deleting Notes.
def TaskList(request):
if request.method == 'GET':
tasks = Task.objects.all().order_by('-updated')
context = {'tasks':tasks}
return render(request, 'base/index.html', context)
if request.method == 'POST':
task = Task.objects.create(
body=request.POST.get('body')
)
task.save()
return redirect('tasks')
## ------------------------------------------------------
def TaskDetail(request, pk):
if request.method == 'GET':
task = Task.objects.get(id=pk)
context = {'task':task}
return render(request, 'base/task.html', context)
if request.method == 'POST':
task = Task.objects.get(id=pk)
task.body = request.POST.get('body')
task.save()
return redirect('tasks')
## ------------------------------------------------------
def TaskDelete(request, pk):
task = Task.objects.get(id=pk)
if request.method == 'POST':
task.delete()
return redirect('tasks')
context = {'task':task}
return render(request, 'base/delete.html', context)
Keeping our class based views raw
Class based views have a level of complexity to them not because they are difficult to use, but because there is a layer of abstraction to them that makes it difficult to understand exactly what's going on and what we need to do to modify them. Django provides us with a number of built in views to use which makes for rapid development, but before you know the ins and outs of the views this can actually make thngs confusing since there is a lot of magic under the hood.
So instead of using the built in views, I will keep things raw and only extend the base view Django gives us and write all the logic from scratch so you can see how class based views compare and differ from function based views.
A few things about class based views
Before we get started there's a few things I want you to know about class based views.
Extending the base View class
Every class based view extends the base View
class. Since we are not using any other built in views make sure that you import View
and pass in "View" to each class:
from django.views import View
...
class OurView(View):
Separation by http methods
With class based views we separate our code into HTTP verbs. So instead of having to do something like if request.method == 'POST'
, we simply modify the post
method provided by the View
class and let that method take care of everything that happens on a post
request. The same goes for get
requests.
Ex:
class OurView(View):
def get(self, request):
pass
def post(self, request):
pass
Let's get started with our first view.
TaskList View
Let's comment out the TaskList
view and rebuild it from scratch.
We'll rewrite the view as a class now and inherit from the View
class. Let's also add two methods to this new class (get
& post
) and make sure to pass in self
before request
in each method.
Once we have the class and two methods, lets extract the logic from our function based view and add it to the new class according to each http method like so:
from django.views import View
....
class TaskList(View):
def get(self, request):
tasks = Task.objects.all().order_by('-updated')
context = {'tasks':tasks}
return render(request, 'base/index.html', context)
def post(self, request):
task = Task.objects.create(
body=request.POST.get('body')
)
task.save()
return redirect('tasks')
Now to use this view we need to reference the class in urls.py and then use the as_view()
method.
path('', views.TaskList.as_view(), name="tasks"),
And just like that, we converted our first function based view into a class based view!
TaskDetail View
Now lets do the same for TaskDetail
. Again, we will comment out our function based view and extract and separate all the logic we have into the http methods.
class TaskDetail(View):
def get(self, request, pk):
task = Task.objects.get(id=pk)
context = {'task':task}
return render(request, 'base/task.html', context)
def post(self, request, pk):
task = Task.objects.get(id=pk)
task.body = request.POST.get('body')
task.save()
return redirect('tasks')
Then add as_view()
to the url path for when we call this view.
path('<str:pk>/', views.TaskDetail.as_view(), name="task"),
TaskDelete View
At this point I'm sure you're starting to see the pattern, so lets do the same as before with the Delete view.
class TaskDelete(View):
def get(self, request, pk):
task = Task.objects.get(id=pk)
context = {'task':task}
return render(request, 'base/delete.html', context)
def post(self, request, pk):
task = Task.objects.get(id=pk)
task.delete()
return redirect('tasks')
path('<str:pk>/delete/', views.TaskDelete.as_view(), name="delete"),
So lets recap what we did.
For each view we:
- Changed
def
toclass
- Extended the base
View
class - Separated logic by
http
methods and addedself
before request - Added
as_view()
to each view inurls.py
Top comments (4)
Interesting. I personally prefer not to use class based views at all, but you make good arguments.
See here
=> spookylukey.github.io/django-views...
for a totally different opinion :)
Dennis Ivy to the world!
your light has really illuminated my path on this journey. Keep the great work chief.
thanks
Amazing write up