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

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