Django Class Based Views
Django Class Based Views#
Remember that things with similar functionality and only slight differences can be reused by the use of classes in python, well we can do the same with django views.
- class
View
-
class
genericViews
- built onview
from django.views.generic import view
Have the class extend View
class HelloWorldView(View):
def get(self, request):
return HttpResponse("Hello World!")
Create a get
method that takes self
because it is an instance and request
just like function based views
The url
is a bit different as it must point to a callable (function) not a class
So we use class.as_view()
which handles instanitating class
url(r'^hello/$', views.HelloWorldView.as_view(), name='hello')
as_view()
is a class method so it does not need an instance of a class to run, it creates the instance. Like setting up request and then runs dispatch
method which runs the HTTP methods get
TemplateView#
There is also a TemplateView
class
from django.views.generic import View, TemplateView
class HomeView(TemplateView): template_name = ‘home.html’
Override context data#
class NameView(TemplateView):
template_name = 'name.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context['name'] = 'hello'
return context
DetailView and ListView#
Using defaults saves time
from django.views.generic import ListView, DetailView
class TeamListView(ListView):
model = models.Team
context_object_name = 'teams'
class TeamDetailView(DetailView):
model = models.Team
Sticking with the defaults - speeds you up
CreateView, UpdateView, DeleteView#
from django.views.generic import (
ListView, DetailView,
CreateView, UpdateView, DeleteView
)
Need to define model
and fields
for CreateView
Also add a get_absolute_url
method to the model
Reverse Lazy#
from django.core.urlresolvers import reverse_lazy
So reverse lazy is ealuated when view is instantiated not when file is created If view does not exist it does not matter
Override queryset#
You can set queryset
variable in a class based view, but queryset
is mutable so you must write .all()
at end to get a fresh one
Best way is to override get_query_set
method
So you can for example only delete a team if you are a superuser
def get_queryset(self):
if not self.request.user.is_superuser:
return self.model.objects.filter(coach=self.request.user)
return self.model.objects.all()
You can also change the intial data:
def get_initial(self):
intial = super().get_initial()
initial['coach'] = self.request.user.pk
return initial
An Excellent resource to use to know the method is CCBV
Combining class-based views#
Example extending from both: DetailView
, UpdateView
class TeamDetailView(DetailView, UpdateView):
model = models.Team
fields = ('name', 'practice_location', 'coach')
template_name = "teams/team_detail.html"
You should explicitly set template_name
when combining, so django knows what is the template to use. Otherwise I think it takes the first.
Problem that sometimes happen is that the super()
methods work left to right.
So the left class being extended is run first then right class.
CreateView
looks for a object and ListView
only has multiple objects.
Sometimes you can just reverse the order
Mixins#
Sometimes you need to override or manipulate a class
But to do it in many locations use a little repeatable class: Mixins
check ccbv.co.za for docs on class based views and mixins
To enforce authentication use the loginRequiredMixin
from django.contrib.auth. import LoginRequiredMixin
Then inherit from in a class based view on the left:
class TeamCreateView(LoginRequiredMixin, CreateView):
...
Then it will automatically redirect to the accounts/login
page
Read the Mixins in Class based views docs
Creating a Custom Mixin#
Create a file called mixins.py
Create the mixin class:
class PageTitleMixin:
page_title = ""
def get_pagetitle(self):
return self.page_title
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["page_title"] = self.get_pagetitle
return context
Import it:
from . import mixins
Extend from the mixin:
class TeamCreateView(mixins.PageTitleMixin, CreateView):
page_title = "Create a new team"
def get_pagetitle(self):
obj = self.get_object()
return "Update {}".format(obj.name)
Now you can set the page_title
in the class based view