[Answer]-Django LoginFormMiddleware breaks with class based views

1👍

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.

0👍

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.

Leave a comment