Django Built-In Template Tags in Views

Django’s built-in template tags are simply Python functions which means they can be used in places other than templates such as class-based views.

Here’s an example using pluralize:

from django.views.generic import ListView
from django.template.defaultfilters import pluralize
from myapp.models.Product
class MyView(ListView):
queryset = Product.objects.all()
template_name = 'product.html'
context_object_name = 'products'
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
context['title'] = len(context['products']) + ' Product' + pluralize(context['products']) + ' Found'
return context

view raw
view.py
hosted with ❤ by GitHub

Django Form Wizard and Getting Data from Previous Steps

I’ve spent the last few days implementing a Django SessionWizardView and ran into what I thought would be a simple problem – how to access the data of a previous form in order to initialize a later form (i.e. use data submitted on step 1 to initialize the step 3 form). Google didn’t turn up much so I turned to the documentation for a solution.

At first glance it seemed simple enough – use get_cleaned_data_for_step or get_all_cleaned_data. The problem with these, I quickly discovered, is that they call get_form which in turn calls get_form_initial (which is where I’m trying to use the submitted form data in the first place). So that was a dead end.

Next up was using get_form_step_data. This method is called before get_form_initial and has access to both the form’s data and cleaned_data properties. The issue with this technique, however, is that it would require the data to be stored in some way for use in get_form_initial (either via a session variable or an instance variable). While this works, it still seemed like there had to be a better way. After all, the wizard is storing this data somewhere already.

So after a bit of digging in the source, the solution was obvious – self.storage.get_step_data(step).

This method allows you to pull the data from any step and will return either None or a MultiValueDict.

class MyWizardView(SessionWizardView):
# this runs for the step it's on as well as for the steps before
def get_form_initial(self, step):
# steps are named 'step1', 'step2', 'step3'
# get the data for step 1
prev_data = self.storage.get_step_data('step1')
some_var = prev_data.get('step1-some_var','')
return self.initial_dict.get(step, {'some_var': some_var})

view raw
views.py
hosted with ❤ by GitHub

One final thing to note is that because get_form_initial is called for every previous step (with that previous step as the step argument), it’s probably likely you’ll need to know what step you’re actually on. You can do this by using self.storage.current_step.

class MyWizardView(SessionWizardView):
# this runs for the step it's on as well as for the step before
def get_form_initial(self, step):
# steps are named 'step1', 'step2', 'step3'
current_step = self.storage.current_step
# get the data for step 1 on step 3
if current_step == 'step3':
prev_data = self.storage.get_step_data('step1')
some_var = prev_data.get('step1-some_var','')
return self.initial_dict.get(step, {'some_var': some_var})
return self.initial_dict.get(step, {})

view raw
views.py
hosted with ❤ by GitHub

If anyone knows of a better way to do this, I’d love to know.

Redirect in Django urls.py

With the addition of generic views, we can easily do a redirect from urls.py. Simply pass the url to the RedirectView and that’s it.

url(r'^my/old/url/$', RedirectView.as_view(url=reverse_lazy('new_url')))

view raw
urls.py
hosted with ❤ by GitHub

Django Mixins – RequireSignInAjax and JSONResponseMixin

So you’ve developed a secure section of your site and now you need to create an AJAX view… but that view needs to be locked down as well.

The RequireSignIn mixin in the previous post returns an HttpResponseRedirect which won’t work in this situation.

First, let’s lock down the view to require the user to be logged in when making an AJAX request.

Here’s what the mixin looks like:

views/mixins/requiresigninajax.py

from django.utils.decorators import method_decorator
from app.lib.ajax import login_required_ajax
class RequireSignInAjax(object):
@method_decorator(login_required_ajax())
def dispatch(self, request, *args, **kwargs):
return super(RequireSignInAjax, self).dispatch(request, *args, **kwargs)

view raw
requiresigninajax.py
hosted with ❤ by GitHub

You’ll notice that I’m making use of a method decorator – login_required_ajax. This decorator simply checks to see if the user is authenticated and if so, allows the request to continue. Otherwise it returns some json containing an error and the proper 401 http status which you could then use to ask the user to login.

Here’s the function:

lib/ajax.py

import json
from django.http import HttpResponse
from django.utils import timezone
from django.utils.dateformat import format
from django.conf import settings
def login_required_ajax(function=None):
# ensure the user is authenticated to access a certain ajax view
# otherwise return a json object notifying the user access is denied
def _decorator(view_func):
def _wrapped_view(request, *args, **kwargs):
if request.user.is_authenticated():
return view_func(request, *args, **kwargs)
else:
now = format(timezone.now(), u'U')
# delete all existing kwargs
# as they cause HttpResponse to throw TypeError: __init__() got an unexpected keyword argument
kwargs = {'content_type': 'application/json'}
response = {
'status': '401',
'message': 'Unknown authentication scheme',
'timestamp': now,
'errorcode': settings.API_ERROR_AUTHENTICATION
}
return HttpResponse(status=401, content=json.dumps(response), **kwargs)
return _wrapped_view
if function is None:
return _decorator
else:
return _decorator(function)

view raw
ajax.py
hosted with ❤ by GitHub

Ok, now we’ve made sure the user is logged in. Let’s add a simple mixin (pulled from the Django docs), to return some json:

mixins/jsonresponse.py

import json
from django.http import HttpResponse
class JSONResponseMixin(object):
# a mixin that can be used to render a JSON response
response_class = HttpResponse
def render_to_response(self, context, **response_kwargs):
# returns a JSON response, transforming 'context' to make the payload
response_kwargs['content_type'] = 'application/json'
return self.response_class(self.convert_context_to_json(context), **response_kwargs)
def convert_context_to_json(self, context):
# this would need to be a bit more complex if we're dealing with more complicated variables
return json.dumps(context)

view raw
jsonresponse.py
hosted with ❤ by GitHub

Finally, you can use these in your view like so…

views/myview.py

from django.views.generic import View
from app.views.mixins.requiresigninajax import RequireSignInAjax
from app.views.mixins.jsonresponse import JSONResponseMixin
class MyView(RequireSignInAjax, JSONResponseMixin, View):
def get(self, request, *args, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))

view raw
myview.py
hosted with ❤ by GitHub

Django Class-based Views with Multiple Forms

By default some of Django’s class-based views support just a single form per view. This, of course, does not always play nicely with what you’re trying to accomplish. I’ve yet to come across a concrete example of how to accomplish this so after a bit of experimentation and some time spent diving into Django’s code, I’ve created a decent workaround. Please let me know in the comments if there’s a better way to do this.

For this example I’ll use a generic UpdateView and two ModelForms. The same technique should work with most of the class-based views as well as regular forms.

I’ll also mention that this handles the validation and submission of either form, but not both forms at the same time. The user is only submitting a single form, so you only need to handle that one.

There’s nothing special about the models or the templates, so let’s look at the view:

from django.views.generic import UpdateView
from django.shortcuts import get_object_or_404
from django.core.urlresolvers import reverse_lazy
from app.models import Model
from app.forms import Form1, Form2
class MyView(UpdateView):
template_name = 'template.html'
form_class = Form1
second_form_class = Form2
success_url = reverse_lazy('success')
def get_context_data(self, **kwargs):
context = super(MyView, self).get_context_data(**kwargs)
if 'form' not in context:
context['form'] = self.form_class(initial={'some_field': context['model'].some_field})
if 'form2' not in context:
context['form2'] = self.second_form_class(initial={'another_field': context['model'].another_field})
return context
def get_object(self):
return get_object_or_404(Model, pk=self.request.session['someval'])
def form_invalid(self, **kwargs):
return self.render_to_response(self.get_context_data(**kwargs))
def post(self, request, *args, **kwargs):
# get the user instance
self.object = self.get_object()
# determine which form is being submitted
# uses the name of the form's submit button
if 'form' in request.POST:
# get the primary form
form_class = self.get_form_class()
form_name = 'form'
else:
# get the secondary form
form_class = self.second_form_class
form_name = 'form2'
# get the form
form = self.get_form(form_class)
# validate
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(**{form_name: form})

view raw
views.py
hosted with ❤ by GitHub

The first thing the view does is set the template, success_url and the two forms. Next we define the get_context_data method which adds either form to the context if it is missing.

UPDATE: There was an issue with the get_context_data method in which the form would not be populated with values from the model if a form was submitted with invalid data (and self.form_invalid was called). The current fix is to manually pass the data to the form using the initial argument.

The get_object method overrides the default behavior and gets a model instance based on a session variable. This isn’t necessary if you’re using the standard method of providing a pk or slug in the URL.

The next change is very important – override the default form_invalid method. The default method returns a single form in the context:

return self.render_to_response(self.get_context_data(form=form))

view raw
views.py
hosted with ❤ by GitHub

Since we have two forms, with different names, we need to switch to using **kwargs instead.

Finally, we override the post method to determine which form was submitted based on the name of the submit button (you could alternatively use a hidden field), validate the form and call form_valid or form_invalid. These methods will save the form and redirect to the success_url, or return to the view and show any errors, respectively.

And for the sake of clarity, the forms in your templates simply need a named submit button:

<form action="{% url 'myview' %}" method="post" enctype="multipart/form-data">
<input type="submit" name="form" value="Submit" />
</form>
<form action="{% url 'myview' %}" method="post" enctype="multipart/form-data">
<input type="submit" name="form2" value="Submit" />
</form>

view raw
template.html
hosted with ❤ by GitHub

Django Generic DetailView without a PK or Slug

By default the generic DetailView expects you to provide a pk or slug in the URL. If it’s missing you’ll get a lovely little error:

Generic detail view UserView must be called with either an object pk or a slug

For the most part this is fine as you’ll usually provide an identifier in the URL. But what if you need to pull the identifier from someplace else such as the session?

To accomplish this, simply override the get_object method:

class UserView(DetailView):
template_name = 'template.html'
#model = User
#context_object_name = 'foo'
def get_object(self):
return get_object_or_404(User, pk=request.session['user_id'])

view raw
views.py
hosted with ❤ by GitHub

Also note that the model is no longer necessary as you’re explicitly calling it in get_object. The object is now available in the template using the model name, in this case user:

{{ user.id }}

view raw
template.html
hosted with ❤ by GitHub

You can override this name by using context_object_name.

Checking if a GET Variable Exists in Django

http://stackoverflow.com/questions/2422055/how-to-check-if-request-get-var-is-none

First, check if the request.GET dict contains a parameter named q:

if request.method == 'GET' and 'q' in request.GET:

view raw
basics.py
hosted with ❤ by GitHub

Now ensure the variable has a value:

q = request.GET['q']
if q:
# Do processing here

view raw
basics.py
hosted with ❤ by GitHub

Multiple Files for Django Views

Separating your views into multiple files works much the same way as models except for one difference – importing the views in __init__.py isn’t necessary unless you want to type a little less.

Without importing them in __init__, you’ll need to do this:

from app.views.blog import Blog

view raw
import.py
hosted with ❤ by GitHub

But if you add them, you can do the following instead:

# import one view
from app.views import Blog
# import a bunch
from app.views import *

view raw
import.py
hosted with ❤ by GitHub

Django Class-based Sign Out Redirect

A typical use case is to provide the user a sign out button which redirects to another page such as the homepage. The following implements this using the generic RedirectView.

conf/urls.py

from app.views.signout import SignOut
url(r'^signout/$', SignOut.as_view(), name='signout'),

view raw
urls.py
hosted with ❤ by GitHub

views/signout.py

from django.views.generic.base import RedirectView
from django.contrib.auth import logout
from django.core.urlresolvers import reverse
class SignOut(RedirectView):
permanent = False
query_string = True
def get_redirect_url(self):
logout(self.request)
return reverse('home')

view raw
signout.py
hosted with ❤ by GitHub

Django Class-based Views

Class-based views were introduced in Django 1.3 and are very useful. This article discusses how to make use of them along with creating a mixin for requiring the user to be authenticated for a specific view.

I’ve tweaked the example a little bit, redirecting to the login page rather than a 404 and separating the mixin into a separate file.

conf/urls.py

from app.views.about import About
url(r'^about/$', About.as_view(), name='about'),

view raw
urls.py
hosted with ❤ by GitHub

views/mixins.py

from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
class RequireSignIn(object):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated():
url = '{0}?next={1}'.format(reverse('signin'), request.path)
return HttpResponseRedirect(url)
return super(RequireSignIn, self).dispatch(request, *args, **kwargs)

view raw
mixins.py
hosted with ❤ by GitHub

views/about.py

from django.views import generic
from app.views.mixins import RequireSignIn
class About(RequireSignIn, generic.TemplateView):
template_name = 'about/index.html'
def get_context_data(self, **kwargs):
ctx = super(About, self).get_context_data(**kwargs)
return ctx

view raw
about.py
hosted with ❤ by GitHub