web-dev-qa-db-ja.com

すべてのページにDjangoログインフォームを配置する

ユーザーがログインしていない場合、ログインフォーム(Django.contrib.authのAuthenticationForm)をサイトのすべてのページに表示したいのですが、ユーザーがログインすると、同じページにリダイレクトされます。エラーがある場合、エラーはフォームと同じページに表示されます。

すべてのテンプレートにフォームを提供するには、コンテキストプロセッサが必要だと思います。しかし、投稿されたフォームを処理するためにすべてのビューも必要ですか?これは、ミドルウェアを作成する必要があることを意味しますか?少し迷ってしまいました。

これを行うための受け入れられた方法はありますか?

46
asciitaxi

わかりました。やっとこれを行う方法を見つけましたが、もっと良い方法があると確信しています。 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」がフォームに追加されたことで、ページに投稿された他のフォームと区別できることに注意してください。また、フォームアクションは「。」です。 auth loginビューの代わりに。

34
asciitaxi

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組み込みビューを使用しているため、この方法では、ログイン用のコンテキストプロセッサを記述する必要はありません。

25
twampss

最も簡単な方法は、おそらく次のようにフォームを手動でベーステンプレートに配置することです。

{% 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に接続されたビューを記述して、通常どおりにフォームを処理します(上記のフォームに一致するフォームオブジェクトを使用)。送信したページと同じページに表示するには、ビューをrequest.META['HTTP_REFERER']にリダイレクトします。

このアプローチにより、ミドルウェアや、コンテキストを介してすべてのテンプレートでフォームを使用できるようにする必要がなくなります。

更新:このアプローチにはいくつかの問題があります。もう少し考える必要があります。うまくいけば、少なくともあなたは正しい方向に行くことができます。

6
John Debs

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クラスを呼び出します。それ以外の場合は、追加のフォームを含むページでエラーが発生します。そのため、[送信]ボタンの値を使用して選択します関連するケース
2
mirdande