Easily Convert Django Function Based Views To Class Based Views
Tags: python, django
Published: 2022-07-25 02:44:00

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](https://youtu.be/-3BN-JMLE0A) and referencing the [source code](https://github.com/divanov11/Easily-Convert-Django-Function-Based-Views-To-Class-Based-Views).

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

```python
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:

```python
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:

```python
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:

```python
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.

```py
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. 

```py
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.

```python
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.

```python
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')
```

```python
path('<str:pk>/delete/', views.TaskDelete.as_view(), name="delete"),
```

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`