This site runs best with JavaScript enabled.

Django: Login Form on Every Page


How to include a login form on every page of a Django app

Up to the point, when it has come to Django user authentication and registrations, I've used django-registration as a plug and play solution. Authentication was frankly something I didn't want to dive into, until now.

It turns out it is not scary at all. For my latest project, I had some space to fill in a free template I found and liked, so I decided to put a login form on every page if the user was not logged in. This is something I have done before in a PHP/Smarty project, but have yet to attempt in Django. It wasn't totally straight forward, so I decided to document it here.

First of all, I put my login form in my base template. I just hand coded the form because I didn't want to message with passing a form object to every single page. My login form code looked something like this:

1{% if user.is_authenticated %}
2<!-- Authenticate account menu -->
3{% else %}
4<h3>Login</h3>
5<form action="/login/" method="post" accept-charset="utf-8">
6 <label for="username">Username</label
7 ><input type="text" name="username" value="" id="username" />
8 <label for="password">Password</label
9 ><input type="password" name="password" value="" id="password" />
10 <p><input type="submit" value="Login ->" /></p>
11</form>
12{% endif %}

I created my own login and logout views as follows:

1def mylogin(request):
2 if request.method == 'POST':
3 user = authenticate(username=request.POST['username'], password=request.POST['password'])
4 if user is not None:
5 if user.is_active:
6 login(request, user)
7 # success
8 return HttpResponseRedirect('/')
9 else:
10 # disabled account
11 return direct_to_template(request, 'inactive_account.html')
12 else:
13 # invalid login
14 return direct_to_template(request, 'invalid_login.html')
15
16def mylogout(request):
17 logout(request)
18 return direct_to_template(request, 'logged_out.html')

And the URL patterns to go with them:

1(r'^login/$', 'minisitetracker.sites.views.mylogin'),
2(r'^logout/$', 'minisitetracker.sites.views.mylogout'),

This all worked fine, except when I wanted to use the @login_requied decorator, which redirected to /accounts/login/?next=(page I was on). So I created a simple redirect which basically told them to use the login form to login.

1urlpatterns += patterns('django.views.generic.simple',
2 (r'^accounts/login/$', 'direct_to_template', {'template': 'login_required.html'}),
3)

Notice the "next" variable that gets passed in though. This is a handy feature that sends the user to the page they were trying to access after they log in. Initially I thought it would be nice to have them stay on the page they were on once logging in, but I didn't want to mess with figuring it out so I ignored it. But I just couldn't ignore the "next" feature. It's not in my nature.

So in PHP and Smarty, it is very easy to get POST and GET variables passed into the page from within the template such as { $smarty.post.var } or { $smarty.get.var }. These variables are not passed to Django automatically. So, for the first time, I wrote my own context processor:

1def login(request):
2 if 'next' in request.GET:
3 return { 'NEXT': request.GET['next'] }
4 else:
5 return { 'NEXT': request.path }

Notice the else statement. Return the request.path by default also allowed me to keep the user on the same screen that he logged in on. Notice the slight update to my login form and function:

1{% if user.is_authenticated %}
2<!-- Authenticate account menu -->
3{% else %}
4<h3>Login</h3>
5<form action="/login/" method="post" accept-charset="utf-8">
6 <label for="username">Username</label
7 ><input type="text" name="username" value="" id="username" />
8 <label for="password">Password</label
9 ><input type="password" name="password" value="" id="password" />
10 <input type="hidden" name="next" value="{{ NEXT }}" />
11 <p><input type="submit" value="Login →" /></p>
12</form>
13{% endif %}}
1def mylogin(request):
2 if request.method == 'POST':
3 user = authenticate(username=request.POST['username'], password=request.POST['password'])
4 if user is not None:
5 if user.is_active:
6 login(request, user)
7 # success
8 if request.POST['next']:
9 return HttpResponseRedirect(request.POST['next'])
10 else:
11 return HttpResponseRedirect('/')
12 else:
13 # disabled account
14 return direct_to_template(request, 'inactive_account.html')
15 else:
16 # invalid login
17 return direct_to_template(request, 'invalid_login.html')

Discuss on TwitterEdit post on GitHub

Share article
Dustin Davis

Dustin Davis is a software engineer, people manager, hacker, and entreprenuer. He loves to develop systems and automation. He lives with his wife and five kids in Utah.

Join the Newsletter



Dustin Davis