もた日記

くだらないことを真面目にやる

Djangoメモ(21) : ユーザー認証を実装する〜ログアウト

Python 3.6.4 Django 2.0.2

A Complete Beginner's Guide to Djangoのチュートリアルを参考にログアウト機能を実装する。


ログアウトビュー

ドキュメントによるとDjangoではログイン、ログアウト、パスワード管理のビューは提供されている(サインアップは提供されていない)のでそれを利用する。ただし、テンプレートに関しては提供されていないので自分で実装する必要がある。

以下のようにURLConfを設定すると、

urlpatterns = [
    path('accounts/', include('django.contrib.auth.urls')),
]

下記URLの全パターンが有効になる。

accounts/login/ [name='login']
accounts/logout/ [name='logout']
accounts/password_change/ [name='password_change']
accounts/password_change/done/ [name='password_change_done']
accounts/password_reset/ [name='password_reset']
accounts/password_reset/done/ [name='password_reset_done']
accounts/reset/<uidb64>/<token>/ [name='password_reset_confirm']
accounts/reset/done/ [name='password_reset_complete']

ただし、チュートリアルでは順番に実装していっているためログアウトビューのみを設定する。
myproject/urls.pylogout/の行を追加。

from django.contrib import admin
from django.conf import settings
from django.urls import path, include
from django.contrib.auth import views as auth_views
from accounts import views as accounts_views
from boards import views

urlpatterns = [
    path('', views.home, name='home'),
    path('signup/', accounts_views.signup, name='signup'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
    path('boards/<int:pk>/', views.board_topics, name='board_topics'),
    path('boards/<int:pk>/new/', views.new_topic, name='new_topic'),
    path('admin/', admin.site.urls),
]

個別に設定する場合はauth_views.LogoutView.as_view()のように設定するが、これはクラスベースビューの書き方とのこと。
次にmyproject/settings.pyにログアウト後のリダイレクト先URLを設定する。

LOGOUT_REDIRECT_URL = 'home'

これで設定は完了で、logout/(例:127.0.0.1:8000/logout/)にアクセスするとログアウトし、homeページにリダイレクトされる。 ログアウト後はCookieのsessionidも消えていることが確認できる。


ログアウトメニュー追加

logout/リンクをクリックできるようにログアウト用のドロップダウンメニューを追加する。
チュートリアルではjQuery, Popper.js, Bootstrap(BootstrapのDropdownsを使うためにはjQuery, Popper.jsが必要)のJavaScriptを利用しているのでそれぞれのサイトからダウンロードする。

jquery.com

popper.js.org

getbootstrap.com

ファイルのダウンロードリンク(右クリックで保存)

ファイルをダウンロードしたら以下のディレクトリ、ファイル構成になるように配置する。

├── static/
│  ├── css/
│  │  ├── accounts.css
│  │  ├── app.css
│  │  └── bootstrap.min.css
│  ├── img/
│  │  └── vintage-leaves.png
│  └── js/
│     ├── bootstrap.min.js
│     ├── jquery-3.3.1.min.js
│     └── popper.min.js
└── templates/

JavaScriptを読み込むようにtemplates/base.htmlの後半に<script>タグを追加する。

{% load static %}<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>{% block title %}Django Boards{% endblock %}</title>
    <link href="https://fonts.googleapis.com/css?family=Pacifico" rel="stylesheet">
    <link rel="stylesheet" href="{% static 'css/bootstrap.min.css' %}">
    <link rel="stylesheet" href="{% static 'css/app.css' %}">
    {% block stylesheet %}{% endblock %}
  </head>
  <body>
    {% block body %}
 省略
    {% endblock body %}
    <script src="{% static 'js/jquery-3.3.1.min.js' %}"></script>
    <script src="{% static 'js/popper.min.js' %}"></script>
    <script src="{% static 'js/bootstrap.min.js' %}"></script>
  </body>
</html>

次にドロップダウンメニューを表示するようにtemplates/base.html<nav>内を以下のように変更する。

<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
  <div class="container">
    <a class="navbar-brand" href="{% url 'home' %}">Django Boards</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mainMenu" aria-controls="mainMenu" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="mainMenu">
      <ul class="navbar-nav ml-auto">
        <li class="nav-item dropdown">
          <a class="nav-link dropdown-toggle" href="#" id="userMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
            {{ user.username }}
          </a>
          <div class="dropdown-menu dropdown-menu-right" aria-labelledby="userMenu">
            <a class="dropdown-item" href="#">My account</a>
            <a class="dropdown-item" href="#">Change password</a>
            <div class="dropdown-divider"></div>
            <a class="dropdown-item" href="{% url 'logout' %}">Log out</a>
          </div>
        </li>
      </ul>
    </div>
  </div>
</nav>

ドロップダウンメニューが表示されるようになるのでLog outをクリックしてみる。

f:id:wonder-wall:20180319224130p:plain

ログアウトは成功し、ユーザー名の部分が表示されなくなる。

f:id:wonder-wall:20180319224308p:plain

これでは表示がいまいちなので、ログアウト時には別のボタンを表示するようにtemplates/base.html<nav>内を改良する。

<nav class="navbar navbar-expand-sm navbar-dark bg-dark">
  <div class="container">
    <a class="navbar-brand" href="{% url 'home' %}">Django Boards</a>
    <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#mainMenu" aria-controls="mainMenu" aria-expanded="false" aria-label="Toggle navigation">
      <span class="navbar-toggler-icon"></span>
    </button>
    <div class="collapse navbar-collapse" id="mainMenu">
      {% if user.is_authenticated %}
        <ul class="navbar-nav ml-auto">
          <li class="nav-item dropdown">
            <a class="nav-link dropdown-toggle" href="#" id="userMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
              {{ user.username }}
            </a>
            <div class="dropdown-menu dropdown-menu-right" aria-labelledby="userMenu">
              <a class="dropdown-item" href="#">My account</a>
              <a class="dropdown-item" href="#">Change password</a>
              <div class="dropdown-divider"></div>
              <a class="dropdown-item" href="{% url 'logout' %}">Log out</a>
            </div>
          </li>
        </ul>
      {% else %}
        <form class="form-inline ml-auto">
          <a href="#" class="btn btn-outline-secondary">Log in</a>
          <a href="{% url 'signup' %}" class="btn btn-primary ml-2">Sign up</a>
        </form>
      {% endif %}
    </div>
  </div>
</nav>

{% if user.is_authenticated %}でログイン中であるかを判定し、ログイン中の場合はドロップダウンメニューを表示し、ログアウト時はログイン、サインアップボタンを表示するようにしている。

f:id:wonder-wall:20180319224525p:plain


まとめ

  • Djangoではログイン、ログアウト、パスワード管理のビューが提供されている
  • include('django.contrib.auth.urls')で認証関連のビューを全部読み込める
  • auth_views.LogoutView.as_view()のように個別に設定できる
  • LOGOUT_REDIRECT_URL = 'home'でログアウト後のリダイレクト先を指定
  • BootstrapのDropdownsでログアウトのドロップダウンメニューを作成
  • テンプレートでは{% if user.is_authenticated %}でログイン状態を判定