BUILDING A SIMPLE APP WITH DJANGO'S CLASS BASED VIEWS.
Tags: webdev, django, programming, tutorial
Published: 2022-07-29 04:21:55

**Part III**

This is the concluding part of a **3 part** series in [part I](https://dev.to/osahenru/building-a-simple-app-with-djangos-function-based-views-2729) we took a holistic view on the concepts of function based views and class based views and some disparities between these concepts, in part II we built an app using a function based view and in this concluding part we will be building same application with same functionalities but this time with a class based view approach.

As a prerequisite for getting the best out of this article will be to have a working understanding of Django’s Model Views Template structure, how to start a project and an application as well and lastly a basic understanding of Django's function based views.

**CLASS BASED VIEWS**
Class based views are simply Django views written as python classes, they provide an object-oriented way of your view codes.

One major advantage class based view has is it’s use of inheritance; you can inherit another class and it can be modified for different uses

**Below are some benefits that come with a class based views approach.**

**1. Inheritance; reusable and DRY(don't repeat yourself) codes.**
**2. Eliminate redundant codes.**
**3. Separate each code by POST or GET method.**

Class based views are really powerful and great but come at the expense of simplicity

Even as minor as omitting a forward slash in your `urls.py` can result in an error, class based views forces you to do things the right way.

**View Class**
It’s okay to say this is the grand Daddy of all other views, the `ListView`, `UpdateView`, `DetailView`... at one point all inherit from this view.

**TaskList view class**
```
class TaskList(LoginRequiredMixin, ListView):
    model = Task
    template_name = '../templates/tasklist.html'
    context_object_name = 'tasks'
    # login_url = 'login' An alternative to LOGIN_URL = 'login' in settings.py

    def get_context_data(self):
        context = super(TaskList, self).get_context_data()
        context['tasks'] = context['tasks'].filter(user=self.request.user)
        context['count'] = context['tasks'].filter(completed=False).count()

        search_input = self.request.GET.get('search-area') or ''
        if search_input:
            context['tasks'] = context['tasks'].filter(title__icontains=search_input)
            context['search_input'] = search_input
        return context
```
This class Inherits from the `LoginRequiredMixin` and the `Listview`

`LoginRequiredMixin` needs a login url to send non authenticated users to, you can achieve this in two ways
1. add `login_url = '/login/'` to every class where every `LoginRequiredMixin` is needed.
2. add a default `LOGIN_URL = 'login'` to `settings.py` where every login url inherits from

**Difference between** `login_url` **and** `redirect_field_name`
`login_url`
Deals with the url you want a user to be taken to if a certain condition is met or not.
`redirect_field_name`
Deals with a field box which is set to a default parameter of `=next`

Just as the name, the user needs to be a logged in user to view the template of this view function.

Most class based view (if not all) must have a model attribute.

the default template name for this view is `task_list.html` we override that by creating a directory for all templates and create a template file `tasklist.html`

the default context name for objects of this view is `object_list` we also override that and give our context object the name `tasks`

`get_context_data` is similar to function based views context dictionary `context = {}`

In a class based view approach we have two ways of passing the data into a context.
1. `get_context_data`
2. `extra_context`
In this article we make use of nos.1

We make use of the `get_context_data` function when we want to modify the objects in our template(to add extra elements to our templates).

We filter the task by the requested user, and also filter the requested user task by it's complete status `completed=False`, we count this filtered attributed and save the object.

**Search Functionality**
we add a search function to our task as we did in our function based view so, the request method from our search form is either going to have an actual word search or an empty search, we want same action performed for both conditions;

So, if a search(empty or not) is made filter the tasks context but this time by the `title` and include the `search_input` to the context dictionary

`title__icontains=search_input`, if the user performs a search we want to filter the search this time by the title.


`icontains` **V** `contains`
`icontains` will filter the object regardless of the case(upper of lower) of the string, while `contains` makes a search that is case sensitive.

We make use of the context dictionary for objects we intend to use with our template engine.

**TaskDetail view function**
```
class TaskDetail(LoginRequiredMixin, DetailView):
    model = Task
    template_name = '../templates/taskdetail.html'
    context_object_name = 'task'
```

Inherits from the `DetailView`, approaches like the class based views help reduce the amount of DRY codes, again we define a model attribute, a template and a name for our context objects.

This view function takes in a `PrimaryKey` id, we don't include it when creating our function, Django's class based view handles that for us, we just pass it in our `urls.py` file.

**TaskEdit view function**
```
class TaskEdit(LoginRequiredMixin, UpdateView):
    model = Task
    template_name = '../templates/taskedit.html'
    fields = ['title', 'description', 'completed']
    success_url = reverse_lazy('tasklist')
```
Inherits from the `UpdateView`, this view function has a fields attribute and also returns an instance of a submitted form. 

fields as the name imply are the **HTML** fields that'll be rendered in the frontend of our application, we don't need to create an instance of the this form, again Django's class based view handles that for us, what a beauty!

After a successful form has been created, where do we redirect the user to? this time we use Django's class based views `reverse_lazy` to redirect the user back to the urls they were coming from.

*TaskDelete view function*
```
class TaskDelete(LoginRequiredMixin, DeleteView):
    model = Task
    template_name = '../templates/taskdelete.html'
    context_object_name = 'task'
    success_url = reverse_lazy('tasklist')
```
This is similar to `TaskEdit` with a few peculiarities; **does not  have the fields attribute** and also **doesn’t return an instance of any submitted form**, it takes in the `PrimaryKey` of the task to be deleted and under the hood performs the `delete()` function for us which we do not see.

**TaskCreate view function**
```
class TaskCreate(LoginRequiredMixin, CreateView):
    model = Task
    template_name = '../templates/taskcreate.html'
    fields = ['title', 'description']
    success_url = reverse_lazy('tasklist')
    
    def form_valid(self, form):
        form.instance.user = self.request.user
        return super(TaskCreate, self).form_valid(form)
```
Very similar to `TaskEdit` as a matter of fact they both have same parent `views.generic.edit` we add a tweak to this class, by  modifying the form; we assign each user to it’s task created.

**The super function can be used to gain access to inherited methods-from a parent or sibling class- that has been overwritten in a class object, all this is big grammer what this means is that the `super()` function allows the function in which it is been used to make use of methods in its above class**

**CustomLogin view function**
```
class CustomLogin(LoginView):
    redirect_authenticated_user = True
    template_name = '../templates/login.html'
    form_class = CleanForm
    # next_page = 'tasklist' Alternative to the get_success_url

    def get_success_url(self):
        return reverse_lazy('tasklist')
```
Inherits from the `LoginView` and has  `AunthenticationForm` as it's default `form_class`

We want to disable already authenticated users from logging again, we achieve this by setting the `redirected_authenticated_user` attribute of this class to *True* so once a user has been authenticated, they can’t be authenticated again.

We perform a clean operation before we log the user in, more info on how we did this down below.

`next_page =` handles where an authenticated user should be taken to, this attribute and the `get_success_url` perform the same function.

**Register view function**
```
class Register(FormView):
    form_class = UserCreationForm
    success_url = reverse_lazy('logout')
    template_name = '../templates/register.html'
    
    # Function for a valid form
    def form_valid(self, form):
        user = form.save(commit=False)
        user.username = user.username.lower()
        user = form.save()
        if user is not None:
            login(self.request, user)
            return redirect('logout')
        return super(Register, self).form_valid(form)

    # Function to redirect authenticated user
    def get(self, request):
        if self.request.user.is_authenticated:
            return redirect('tasklist')
        return super(Register, self).get(request)
```
Inherits from the `FromView` model also has `UserCreationForm` as it’s form class for creating users in Django.

We make a few modification to our `UserCreationForm`, if the form is valid, we commit a false save to clean our data, to ensure it’s not case sensitive, save the user after, return this created user to the logout page once they have been created.

We want to be able to also redirect authenticated users, although this view class doesn’t come with that attribute hence, we create one using `get(self, request)` redirect the authenticated user back to the `tasklist` page if they try to register after they have been authenticated, and lastly return the class with the super function that just continues with everything this function was meant to do.

```
CleanForm in forms.py
from django.contrib.auth.forms import AuthenticationForm


class CleanForm(AuthenticationForm):

    def clean_username(self):
        return self.cleaned_data['username'].lower()
```

Since we know that our `LoginView` has a from class called `AuthenticationForm` we want to tweak that form a bit by cleaning the username just as we did with our function based views.


Miscellaneous
Self
`self` is used to represent the instance of a class; one is able to access the attributes and method in which this self function is used.

Mixins
Are simply classes that contain methods to be used by other classes. We use mixins when we want to add a particular feature that will be made available in multiple child objects, they should be used to take care of one specific task, in function based views we have `decorators` for this.

Difference between `ModelForm`, `FormView` and `CreateView`
`ModelForm`
Django's `ModelForm` is used to directly convert a model into a Django form
`FormView`
A view that can be used for various purpose and not restricted to creating objects does not necesarily inherit from your custom made model, it usually to display and verify a form(when registering in class based views is where I mostly use it). it fields don't necessarily need to be created in `models.py`
`CreateView`
Mostly used when you want to create objects(not restricted to this alone), makes use of an in-built `ModelForm`

Below is the github repo to the  full project.
[kanyi-v-1.1](https://github.com/TimOsahenru/kanyi-v-1.1.git)

Below is the link to where this project is been deployed.
[Kanyi-v-1.1](https://kanyi-v-11-production.up.railway.app/)