Part III
This is the concluding part of a 3 part series in part I 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
- add
login_url = '/login/'
to every class where everyLoginRequiredMixin
is needed. - add a default
LOGIN_URL = 'login'
tosettings.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.
get_context_data
-
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
Below is the link to where this project is been deployed.
Kanyi-v-1.1
Top comments (0)