As per a few other SO answers, I\'m using middleware to show a login form on every page of my project, such that a user can login in-place. I am aware some frown upon this, but
To finalize this question, Alasdair's answer is spot on. The final code I'm using is
from MyProj.forms import MyProjTopLoginForm
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
#Notes: http://stackoverflow.com/questions/2734055/putting-a-django-login-form-on-every-page
class LoginFormMiddleware(object):
def process_request(self, request):
# if the top login form has been posted
if request.method == 'POST' and 'top_login_form-username' in request.POST:
# validate the form
form = MyProjTopLoginForm(prefix="top_login_form", data=request.POST)
if form.is_valid():
# log the user in
from django.contrib.auth import login
login(request, form.get_user())
# if this is the logout page, then redirect to /
# so we don't get logged out just after logging in
if reverse('logout') in request.get_full_path():
return HttpResponseRedirect('/')
# Redirect to the same page after successfully handling the login form data.
return HttpResponseRedirect('')
# We could also do:
# request.method = 'GET'
# instead of a redirect, but if a user refreshes the page, they'll get prompted to re-send post data,
# which is super annoying.
else:
form = MyProjTopLoginForm(request, prefix="top_login_form")
# attach the form to the request so it can be accessed within the templates
request.top_login_form = form
class LogoutFormMiddleware(object):
def process_request(self, request):
if request.method == 'POST' and request.POST.has_key('logout-button') and request.POST['logout-button'] == 'logout':
from django.contrib.auth import logout
logout(request)
# Same as above. Handle the post data, then redirect to a new GET (not POST) request.
return HttpResponseRedirect('')
This solves the original issue, as well as issuing a redirect upon successful handling of (login, logout) data so that a user-triggered refresh doesn't prompt to resend the form.
In class based views, the default dispatch method attempts to delegate to a method corresponds to the HTTP method.
The TemplateView only implements a get() method, so it only works for GET requests. When you log in with a POST request, the dispatch method looks for TemplateView.post() method. Because this does not exist, it returns an HTTP Error 405 (Method not allowed).
In your middleware, I suggest that you redirect to the same url after a successful login. This Post/Redirect/Get pattern is good advice in general. The browser will follow the redirect, and fetch the IndexView with a GET request, which will be successful.
if form.is_valid():
# log the user in
from django.contrib.auth import login
login(request, form.get_user())
# if this is the logout page, then redirect to /
# so we don't get logged out just after logging in
if reverse('logout') in request.get_full_path():
return HttpResponseRedirect('/')
# redirect to the same url after a successful POST request.
return HttpResponseRedirect('')
Finally, the browser might display a blank page, but there's more information that can be useful for debugging. The Django dev server will show that it returned a 405 error code. Use the developer toolbars for your browser, which should show you the description of the error code 405 METHOD NOT ALLOWED, and the Allow:get, head header which tells you that the view does not allow post requests.