Photo by NASA on Unsplash

Django CBVs: Top 8 most common methods to overwrite

Adrien Van Thong

--

Django’s class-based-views, or CBVs for short, provide an incredibly powerful framework which help developers to very quickly get to work on writing high-quality business logic while reducing boilerplate code.

There are many methods in the various CBVs which I often overwrite for my projects — in this article I will go over the top methods I overwrite most often, and describe what they are used for and when is an appropriate time to do so.

get_context_data()

This method appears in every CBV and is by far the most-overwritten method in my CBVs (This is likely also true for most Django app developers too)

This method plays an incredibly important role — it is responsible for compiling and organizing all the relevant data (a.k.a. “context”) and sending it to the template for rendering. In most CBVs, by default this includes the record(s) for the referenced model.

By overwriting this class, you can insert additional variables to send to the template, for example:

def get_context_data(self, **kwargs):
# Get the current context from the parent's get_context_data method
context = super().get_context_data(**kwargs)

# Insert new entries to the context & return it:
context['my_new_entry'] = 'Hi! I am new extra context!'
context['some_other_model'] = OtherModel.objects.all()

return context

New variables can be accessed from the templates by their keys in the context object that was returned above:

Here is some additional context: <br />
{{ my_new_entry }}

Here are entries from a different model:
<ul>
{% for other_model in some_other_model %}
<li>{{ other_model }}</li>
{% endfor %}
</ul>

form_valid()

The form_valid method, and its twin form_invalid are only relevant for CBVs inheriting from the FormView class or other related Mixins (i.e. CreateView). The form_valid method is invoked after the user submits the relevant form and it has been validated, and is responsible for performing any necessary follow-on action(s) with that data.

This responsibility makes the form_valid method a perfect candidate for overwritting when needing to perform additional custom business logic when a user submits a form. For example: sending an E-mail to users, adding an entry to a transaction log, automatically filling in other fields on the referenced record, etc.

from my_module import my_sendmail

class SampleModelCreateView(CreateView):
model = SampleModel

def form_valid(self, form):
self.object = form.save()
# Automatically generate the value of a field based on two other fields:
self.object.field3 = self.object.field1 + self.object.field2
# Automatically populate the value of a field based on current user:
self.object.created_by = self.request.user
self.object.save()
# Send an E-mail about the new record:
my_sendmail(f'{self.request.user} created a new sample model: {self.object}')
return super().form_valid(form)

get_object()

This method is relevant for any CBV operating on a single record for a Model, for example DetailView or UpdateView to name a couple. This method is responsible for fetching the specific record of the corresponding model (typically there is an associated kwarg).

An example of overwriting the get_object method based on a URL slug (the key name needs to match what is defined in urls.py)

def get_object(self, queryset=None):
slug = self.kwargs.get('slug')
return SampleModel.objects.get(sample_attr=slug)

get_queryset()

This method is useful for the CBV which operate on a Model, and operates on multiple records, for example ListView — and is very closely related to the queryset field. In fact, in most cases it is more appropriate to simply use the queryset field to define a queryset, unless more advanced logic requiring the use of a method is necessary.

Unlike the get_object method, which returns only a single record, get_queryset returns multiple records as a QuerySet (as the name implies).

This can be extremely useful when looking to modify the query based on additional GET/POST params, or based on the currently logged in user, for example:

def get_queryset(self):
return super().get_queryset().filter(assignee=self.request.user)

get_form()

This method is only relevant for CBVs which act upon forms, and is responsible for returning an instance of the form class defined by the form field on the CBV.

Overwriting this method can be extremely useful for dynamically modifying the form class on-the-fly, for example populating the contents of a dropdown, or adding/removing certain form elements based on specific conditions, etc.

Notable exception is setting default/initial values for the form fields — generally it is more appropriate to overwrite the get_initial() method for that purpose.

In the example below, we’ll dynamically change whether to hide one of the form fields based on a set of abstract conditions:

class SampleFormView(FormView):
def get_form(self, form_class=None):
form = super().get_form(form_class)
# Hide this field from the user under specific conditions:
if some_condition:
form.fields['hide_this_field'].widget = forms.HiddenInput()
return form

get_initial()

Like in the prior section, this method is also only relevant for CBVs which manage forms, and this method is responsible for setting the initial values on the form fields when the form is displayed to the user for them to fill in. This method should return a dictionary where the keys represent the field names and the values represent the initial value for that corresponding field.

Generally statically-assigned initial values on forms should be set on the corresponding Form class, though this method can come in handy for dynamically-generated initial values.

This can also come in handy when looking to overwrite the default property on model fields for specific views, for example:

class SampleCreateView(CreateView):
model = SampleModel

def get_initial(self):
initial = super().get_initial()
initial['my_field1'] = 'Sample initial value'
initial['my_field2'] = 'Another initial value' if some_condition else 'some other value'
return initial

get_success_url()

This method, and the related success_url field, are responsible for determining where to send the user after the user-submitted form has been successfully processed.

This method is only applicable for CBVs operating on forms, i.e. FormView or UpdateView (to name just a couple) and their related Mixins. Also related is the success_url class field which provides the same functionality. Like the get_queryset method, it is better to use the corresponding field whenever possible.

One use case for overwriting the method instead of using the field would be to determine the success URL based on data provided by the user, or leveraging a related object’s get_absolute_url method, for example:

class SampleCreateModelView(CreateView):
def get_success_url(self):
return self.object.sample_related_model.get_absolute_url()

test_func()

This method is only applicable for CBVs inheriting from the UserPassesTestMixin class, and extremely useful to overwrite when determining which conditions that users are able to access this view. The method returns True if the current user can access this view class, and False otherwise.

The most common use case for this is to limit access for a particular view to only Django admins, for example:

from django.contrib.auth.mixins import UserPassesTestMixin

class AdminOnlyView(UserPassesTestMixin, DetailView):
def test_func(self):
return self.request.user.is_superuser

These are the methods I overwrite the most often when building class based views. Are there any you overwrite that aren’t listed here? Sound off in the comments!

--

--