DEV Community 👩‍💻👨‍💻

Cover image for Easily Convert Django Function Based Views To Class Based Views
Dennis Ivy
Dennis Ivy

Posted on

Easily Convert Django Function Based Views To Class Based Views

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)
Enter fullscreen mode Exit fullscreen mode

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):
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

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"),
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode

Then add as_view() to the url path for when we call this view.

path('<str:pk>/', views.TaskDetail.as_view(), name="task"),
Enter fullscreen mode Exit fullscreen mode

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')
Enter fullscreen mode Exit fullscreen mode
path('<str:pk>/delete/', views.TaskDelete.as_view(), name="delete"),
Enter fullscreen mode Exit fullscreen mode

So lets recap what we did.

For each view we:

  • Changed def to class
  • Extended the base View class
  • Separated logic by http methods and added self before request
  • Added as_view() to each view in urls.py

Top comments (4)

Collapse
dmerejkowsky profile image
Dimitri Merejkowsky

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

Collapse
osahenru profile image
Osahenru

Dennis Ivy to the world!
your light has really illuminated my path on this journey. Keep the great work chief.

Collapse
fahadhossien72 profile image
fahadhossien72

thanks

Collapse
griffin_code profile image
Gabriel Usen

Amazing write up

🌱 DEV runs on 100% open source code that we started called Forem.

 
You can contribute to the codebase or host your own.