Skip to content

Instantly share code, notes, and snippets.

@mikofski
Last active August 29, 2015 14:05
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mikofski/249279b7380cded60a82 to your computer and use it in GitHub Desktop.
Save mikofski/249279b7380cded60a82 to your computer and use it in GitHub Desktop.
Django view utilities to add GET parameters to context and to use a Bootstrap navbar login template
from functools import wraps
from django.contrib.auth.views import login as auth_login
from django.contrib.auth.decorators import login_required
from django.http import HttpResponse, HttpResponseRedirect, QueryDict
from django.core.urlresolvers import reverse, resolve
from django.template.response import TemplateResponse
def add_GET_to_context(*keys):
"""
Check view's URL GET query string for keys and add values to context data
if view response is ``TemplateResponse``.
"""
def outer_wrapper(view):
@wraps(view)
def inner_wrapper(request, *args, **kwargs):
response = view(request, *args, **kwargs)
try:
for k in keys:
response.context_data[k] = request.GET.get(k)
finally:
return response
return inner_wrapper
return outer_wrapper
# For example to check any url for a GET query string ?error=whatever
# use @add_GET_to_context('error') and make sure the view returns a
# TemplateResponse, then it will add the error's value to the context
# to use in the template.
#
# Example template.html
# {% if error == 'login' %}
# <do> stuff </do>
# {% endif %}
@add_GET_to_context('error')
def home(request):
"""
Home page
"""
context = {'path': request.path, 'user': request.user}
return TemplateResponse(request, 'index.html', context)
# Use Case: Bootstrap navbar login
# Django by default opens registration/login.html when it receives a GET
# to login. To redirect all login attempts to a Bootstrap navbar the following
# function wraps the Django login view in a custom view that edits the response.
# If there is an error it adds it a GET query string. If login was called by a
# GET url pattern then check if it is in the LOGIN_REDIRECTS dictionary, and
# then redirect to a different url with the args listed in the dictionary. This
# is useful if using @login_required(login_url='/somwhere/') which will add
# `next` as a GET query string. But if the original url pattern was called with
# a POST method, this url will fail, so redirect it to a url that won't.
# if auth succeeds then login was called by a POST method, so let it return the
# response without doing anything.
LOGIN_REDIRECTS = {'POST_view_url_1': {'redirect_url': 'GET_view_url_1',
'kwargs': ['key1', 'key2']},
'POST_view_url_1': {'redirect_url': 'GET_view_url_1',
'kwargs': ['key1', 'key2']}}
def login(request):
"""
Replaces ``django.contrib.auth.views.login`` to redirect back to same url
that called
"""
response = auth_login(request)
query_str = QueryDict('', mutable=True) # empty mutable QueryDict
# ducktype response for TemplateResponse to catch login called as GET
# or if auth failed, otherwise it's HttpResponseRedirect when auth succeeds
try:
form = response.context_data['form']
url = response.context_data['next']
match = resolve(url)
if form.errors:
query_str['error'] = 'login'
response = HttpResponseRedirect(url + '?' + query_str.urlencode())
elif match.url_name in LOGIN_REDIRECTS:
kwargs = {k: match.kwargs[k] for k in LOGIN_REDIRECTS['kwargs']}
response = HttpResponseRedirect(reverse(LOGIN_REDIRECTS['redirect_url'],
kwargs=kwargs))
finally:
return response
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment