Putting a django login form on every page

Ok, I eventually found a way of doing this, although I'm sure there are better ways. I created a new middleware class called LoginFormMiddleware. In the process_request method, handle the form more or less the way the auth login view does:

class LoginFormMiddleware(object):

    def process_request(self, request):

        # if the top login form has been posted
        if request.method == 'POST' and 'is_top_login_form' in request.POST:

            # validate the form
            form = AuthenticationForm(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 '/account/logout/' in request.get_full_path():
                    return HttpResponseRedirect('/')

        else:
            form = AuthenticationForm(request)

        # attach the form to the request so it can be accessed within the templates
        request.login_form = form

Now if you have the request context processor installed, you can access the form with:

{{ request.login_form }}

Note that a hidden field 'is_top_login_form' was added to the form so I could distinguish it from other posted forms on the page. Also, the form action is "." instead of the auth login view.


Using django.contrib.auth, you can put the form code in the base template like so:

<form method="post" action="{% url auth_login %}">
    {% csrf_token %}
    <p><label for="id_username">Username:</label> <input id="id_username" type="text" name="username" maxlength="30" /></p>
    <p><label for="id_password">Password:</label> <input type="password" name="password" id="id_password" /></p>

    <input type="submit" value="Log in" />
    <input type="hidden" name="next" value="" />
</form>

All you need to do is modify the next value so instead of:

<input type="hidden" name="next" value="" />

It will now be:

<input type="hidden" name="next" value="{{ request.get_full_path }}" />

To access the request object, make sure you include

'django.core.context_processors.request'

in your template context processors. This way you don't have to write any context processors for logins since you are using the Django built-in views.


The easiest way is probably to put the form in manually in a base template like so:

{% if user.is_authenticated %}
    <form action="{% url login %}" method="POST">{% csrf_token %}
        <input id="username-field" name="username" type="text" />
        <input id="password-field" name="password" type="password" />
        <button type="submit">Login</button>
    </form>
{% else %}
    {# display something else here... #}
{% endif %}

and then just write a view hooked up to a URL named "login" to handle the form as you would normally (using a form object that matches the above form). Have the view redirect to request.META['HTTP_REFERER'] to show it on the same page as the one that submitted.

This approach avoids middleware or the need to make a form available to every single template through the context.

Update: There are some problems with this approach; need to think on it a little more. Hopefully it at least gets you going in the right direction.