DjangoBrothers BLOG ✍️

2019/12/07

このエントリーをはてなブックマークに追加
Django テンプレート ログイン

【Django】ログイン後に元にいたページにリダイレクトする方法(LoginViewのnextパラメータの基本)

Webサービスでは、ログイン後に元にいたページなどにリダイレクトさせたい場面が多くあります。

DjangoではLoginViewを利用している場合に、そのようなリダイレクト処理を簡単に実装することができます。

LoginViewの基本

LoginViewとは、Djangoのプロジェクトにおけるログイン周りの処理を担う便利なクラスです。 このクラスを用いることで、Viewの処理を自分で書き上げなくても、基本的なログイン処理を実装することができます。

LoginViewは、以下のような処理を行います。

  • ログインURLがGETメソッドで呼ばれた場合には、ログイン用のフォームをテンプレートで利用できるようにした状態で、ログインページのテンプレートを返します
  • ログインURLがPOSTメソッドで呼ばれた場合にはユーザーのログイン処理を実行します。もしログインに成功すれば、nextというパラメータに指定されたパスのURLにリダイレクトします。もしnextが指定されていない場合には、settings.pyファイルのsettings.LOGIN_REDIRECT_URLに指定したURLにリダイレクトされます。

実際の使い方としては、以下のようにurls.pyファイルでログインページのURLとテンプレートを紐づけるだけです。template_nameで指定したテンプレートファイルを利用することになります。

urls.py

from django.contrib.auth import views as auth_views

path('login/', auth_views.LoginView.as_view(template_name='app/login.html')),

こうすることで、テンプレート側でformnextといったcontext変数(テンプレート側で利用できる変数)を利用できるようになります。 formはログイン用のフォームで、nextはログイン成功時にリダイレクトされるパスになります。詳細は公式ドキュメントを参照してください。

login.html

{% extends "base.html" %}

{% block content %}

<form method="post" action="{% url 'login' %}">
{% csrf_token %}
<table>
<tr>
    <td>{{ form.username.label_tag }}</td>
    <td>{{ form.username }}</td>
</tr>
<tr>
    <td>{{ form.password.label_tag }}</td>
    <td>{{ form.password }}</td>
</tr>
</table>

<input type="submit" value="login">
<input type="hidden" name="next" value="{{ next }}"> <!-- LoginViewに対して現在のURLのnextパラメータを渡す -->
</form>

{% endblock %}

LoginViewを使うことで、ログインページのURLにnextというパラメータを指定すれば、ログイン成功時のリダイレクト先を操作することができることがわかりました。

具体的な例を用いて、どのようにnextパラメータを指定するのかをみてみます。

ログインを促すボタンのURLにパラメータをつける方法

Webサービスでは、未ログインユーザーに対して特定のアクションを促しながらログインページに誘導することは多いと思います。

ログイン後に現在のページにリダイレクトさせたい場合は、そのリンクにnextパラメータを付与します。 現在いるページのパスはrequest.pathで取得することができます。

product.html

<!-- nextパラメータに現在いるページのパスを指定して、ログインページに飛ばす -->
<a href="{% url 'login' %}?next={{ request.path }}">ログイン</a>

login_requiredデコレータを使う方法

login_required()デコレータは、ログインが必要なページにアクセスした際にログインを促すための処理ですが、ログインページへのリダイレクト時にnextというURLパラメータを付与してくれます。

LoginViewを使ったログイン処理では、ログイン成功時にnextパラメータに指定されたパスにリダイレクトするようになっているので、login_required()を利用するだけで元いたページへのリダイレクトができるようになります。

login_required()デコレータは、修飾したView関数に対して以下の処理を行います。

  • もしユーザーがログインしていれば、通常通りページを表示する
  • もしユーザーがログインしていなければ、settings.LOGIN_URLで指定しているログインページのURLにリダイレクトさせ、URLのnextパラメータに元いたページのパスを付与する

実装は以下のとおりです。

login_requiredデコレータを指定する

from django.contrib.auth.decorators import login_required

# ログインが必要なページのView関数にデコレータをつける
@login_required()
def index(request):
    ...
    return render(request, 'app/index.html')

login_required()デコレータによってログインページにリダイレクトされた場合、そのURLにはnext=●●●というパラメータがつきます。

(例)https://sample.com/product/123/というURLがログインが必要なページである場合

  • ログインしていないユーザーがそのページに訪れると、https://sample.com/login/?next=/product/123/にリダイレクトされる(ログインページ)
  • ログインフォームからログイン成功した場合には、元にいたページ(/product/123/)にリダイレクトされる

ログインページ以外のページでログインさせる方法

ログインページ以外のページにログイン用のフォームを実装して、その場でログインさせたい場合もあります。 そうした状況では、LoginViewの機能を利用してログイン処理を行いつつも、ログインが完了したら今現在いるページにリダイレクトさせたいです。

そのような状況では、request.pathというcontext変数を利用します。現在アクセスしているページのパスはrequest.pathから取得することができるので、以下のような方法でフォームを作成することで、そのページ上でログイン処理を行い、同じページにリダイレクトさせることができます。

hoge.html

<form action="{% url 'login' %}" method="post">
    {% csrf_token %}
    <input name="username" type="text">
    <input name="password" type="password">
    <input name="next" type="hidden" value="{{ request.path }}"> <!-- ここでnextのパラメータを指定する -->
    <button type="submit">ログイン</button>
</form>

こちらの方法は自前で特定のページのログインフォームを作っている時などに有効な方法です。