46

如果用户未登录,我希望登录表单(来自 django.contrib.auth 的 AuthenticationForm)出现在我网站的每个页面上。当用户登录时,他们将被重定向到同一页面。如果有错误,错误将显示在与表单相同的页面上。

我想您需要一个上下文处理器来为每个模板提供表单。但是,那么您还需要每个视图来处理发布的表单吗?这是否意味着您需要创建一些中间件?我有点失落。

有没有一种公认的方式来做到这一点?

4

4 回答 4

34

好的,我最终找到了一种方法,尽管我确信有更好的方法。我创建了一个名为 LoginFormMiddleware 的新中间件类。在 process_request 方法中,或多或少地以 auth login 视图的方式处理表单:

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”,因此我可以将其与页面上其他发布的表单区分开来。此外,表单动作是“。” 而不是身份验证登录视图。

于 2010-08-11T13:08:37.013 回答
26

使用 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 内置视图。

于 2010-06-26T17:44:39.120 回答
6

最简单的方法可能是将表单手动放入基本模板中,如下所示:

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

然后只需编写一个连接到名为“login”的 URL 的视图,即可像往常一样处理表单(使用与上述表单匹配的表单对象)。将视图重定向到request.META['HTTP_REFERER']以在与提交的页面相同的页面上显示它。

这种方法避免了中间件或通过上下文为每个模板提供表单的需要。

更新:这种方法存在一些问题;需要多考虑一下。希望它至少能让你朝着正确的方向前进。

于 2010-04-29T02:22:31.240 回答
2

根据 asciitaxi 的回答,我使用这些中间件类来登录和注销:

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 类。否则,当涉及具有更多表单的页面时会出现错误。因此,我使用提交按钮的值来挑选相关案例
于 2013-03-13T23:41:36.377 回答