Размещение формы входа в Django на каждой странице - PullRequest
46 голосов
/ 29 апреля 2010

Мне бы хотелось, чтобы форма входа в систему (AuthenticationForm от django.contrib.auth) появлялась на каждой странице моего сайта, если пользователь не вошел в систему. Когда пользователь входит в систему, они будут перенаправлены на ту же страницу. Если есть ошибка, ошибка будет показана на той же странице с формой.

Полагаю, вам понадобится контекстный процессор для предоставления формы каждому шаблону. Но тогда вам также понадобится каждое представление для обработки опубликованной формы? Значит ли это, что вам нужно создать промежуточное программное обеспечение? Я немного растерялся.

Есть ли приемлемый способ сделать это?

Ответы [ 4 ]

34 голосов
/ 11 августа 2010

Хорошо, в конце концов я нашел способ сделать это, хотя я уверен, что есть лучшие способы.Я создал новый класс промежуточного программного обеспечения под названием LoginFormMiddleware.В методе process_request обработайте форму более или менее так, как это делает представление авторизации:

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

Теперь, если у вас установлен процессор контекста запроса, вы можете получить доступ к форме с помощью:

{{ request.login_form }}

Обратите внимание, что в форму было добавлено скрытое поле is_top_login_form, чтобы я мог отличить его от других опубликованных форм на странице.Также формой действия является «.»вместо представления авторизации входа в систему.

25 голосов
/ 26 июня 2010

Используя django.contrib.auth, вы можете поместить код формы в базовый шаблон следующим образом:

<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>

Все, что вам нужно сделать, это изменить следующее значение, вместо:

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

Теперь оно будет:

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

Чтобы получить доступ к объекту запроса, убедитесь, что вы включили

'django.core.context_processors.request'

в вашем шаблоне контекстных процессоров. Таким образом, вам не нужно писать никаких контекстных процессоров для входа в систему, поскольку вы используете встроенные представления Django.

6 голосов
/ 29 апреля 2010

Самый простой способ - это, вероятно, поместить форму вручную в базовый шаблон, например, так:

{% 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 %}

, а затем просто напишите представление, подключенное к URL-адресу с именем «login», чтобы обработать форму, как вы это обычно делаете (используя объект формы, который соответствует форме выше) Попросите представление перенаправить на request.META['HTTP_REFERER'], чтобы отобразить его на той же странице, что и отправленная.

Этот подход позволяет избежать промежуточного программного обеспечения или необходимости сделать форму доступной для каждого шаблона через контекст.

Обновление : Есть некоторые проблемы с этим подходом; нужно подумать об этом немного больше. Надеюсь, это, по крайней мере, приведет вас в правильном направлении.

2 голосов
/ 14 марта 2013

Исходя из ответа asciitaxi, я использую эти Middleware Classes для входа и выхода:

class LoginFormMiddleware(object):
    def process_request(self, request):
        from django.contrib.auth.forms import AuthenticationForm
        if request.method == 'POST' and request.POST.has_key('base-account') and request.POST['base-account'] == 'Login':
            form = AuthenticationForm(data=request.POST, prefix="login")
            if form.is_valid():
                from django.contrib.auth import login
                login(request, form.get_user())
            request.method = 'GET'
        else:
            form = AuthenticationForm(request, prefix="login")
        request.login_form = form

class LogoutFormMiddleware(object):
    def process_request(self, request):
        if request.method == 'POST' and request.POST.has_key('base-account') and request.POST['base-account'] == 'Logout':
            from django.contrib.auth import logout
            logout(request)
            request.method = 'GET'

Это в моей базе шаблона:

{% if not request.user.is_authenticated %}
    <form action="" method="post">
        {% csrf_token %}
        <p id="login">
            {{ request.login_form.non_field_errors }}
            {% for field in request.login_form %}
                {{ field.errors }}
                {{ field.label_tag}}: {{ field }}
            {% endfor %}
            <input type="submit" name="base-account" value="Login" />
        </p>
    </form>
{% else %}
    <form action="" method="post">
        {% csrf_token %}
        <p id="logout">Logged in as <b>{{ request.user.username }}</b>.
            <input type="submit" name="base-account" value="Logout" />
        </p>
    </form>
{% endif %}

Примечания:

  • Строки request.method = 'GET' необходимы для сайтов с другими формами. Кажется немного, но неудобно, но работает отлично.
  • Поскольку это отображается на каждой странице, мне больше не нужен особый случай выхода из системы, потому что мне просто не нужна отдельная страница выхода из системы
  • Мне нужно некоторое различие в моей форме входа / выхода из системы ПЕРЕД проверкой, если она действительна (таким образом вызывая класс AuthenticationForm. В противном случае будут возникать ошибки при переходе на страницы с большим количеством форм. Поэтому я использую значение кнопки «Отправить» выбрать соответствующие случаи
...