ここまでのレッスンで、投稿(Photoモデル)の設計や画像を表示させることができるようになりました。ただ、現状ではAdminページからしかデータを操作できないので、ユーザーが投稿できるようにしていきましょう。

まずはユーザーの登録・認証機能を実装します。

登録・認証機能についても非常によく使われる機能ですので、Djangoがデフォルトで認証機能をサポートしてくれています。django.contrib.authのところでユーザーの登録や認証に関わる機能が定義されているので、それを利用します。

今回使っているUserモデルは、django.contrib.auth.modelsからインポートして使っています。これと同様に、例えば、django.contrib.auth.formsには、ユーザー登録用の入力フォームやパスワード変更用フォームなどが用意されています。また、django.contrib.auth.decoratorsには、認証に関わる便利なデコレータがあります。これらを、必要に応じて適宜インポートしながら開発を進めていきます。

ログイン機能の実装

まずは、ログイン機能を実装しましょう。 必要なことは、主に以下の2つです。

  1. ログイン画面のURL、ユーザーがログインした後にリダイレクトさせるURL、ログアウトした後にリダイレクトさせるURLを設定する。
  2. ログイン画面のHTMLファイルを作る。

最初に、各種URLの設定をします。app内のurls.pyに以下のように追記してください。

~/PhotoService/app/urls.py

from django.urls import path
from . import views
from django.contrib.auth import views as auth_views  ← 追加

app_name = 'app'
urlpatterns = [
    path('', views.index, name='index'),
    path('users/<int:pk>', views.users_detail, name='users_detail'),
    # 追加
    path('login/', auth_views.LoginView.as_view(template_name='app/login.html'), name='login'),
    path('logout/', auth_views.LogoutView.as_view(), name='logout'),
]    

3行目でインポートしたauth_viewsの中には、LoginView、LogoutViewという関数があるのでこれをそのまま使います。これらの関数が自動的にログイン・ログアウト処理を行ってくれるのです。LoginViewでは、template_nameという引数にログイン画面となるHTMLファイルのパスをに指定します。今回はapp/login.htmlというファイルを作り、これをログイン画面のページとします。login.htmlには、ユーザー名とパスワードを入力するログイン用フォームを配置することになります。

ここまでで、login/のURLにアクセスするとログイン画面が表示され、logout/にアクセスするとログアウトするような設定をしています。ログイン画面は後ほど作成します。

次に、settings.pyに以下の3行を追加してください。

~/PhotoService/PhotoService/settings.py

LOGIN_URL = 'app:login'
LOGIN_REDIRECT_URL = 'app:index'
LOGOUT_REDIRECT_URL = 'app:index'

LOGIN_URLは、ユーザーがログインする時に使うページを設定します。今回の場合、login.htmlのページのことです。仮に、ログイン中のユーザーしか見ることができないページに未ログインのユーザーがアクセスしてきた場合は、LOGIN_URLに設定したページにユーザーがリダイレクトされることになります。

LOGIN_REDIRECT_URLは、ユーザーがログインした時に、最初にリダイレクトさせるURLを指定します。今回は、'app:index'、つまりトップページを指定します。これにより、ログインしたユーザーは最初にトップページを閲覧することになります。

LOGOUT_REDIRECT_URLは、ログアウトしたユーザーをリダイレクトさせるURLです。これも同様、トップページにリダイレクトさせます。

ここで、一度http://127.0.0.1:8000/logout/にアクセスしてみてください。トップページが表示されるはずです。

urls.pyでpath('logout/', auth_views.LogoutView.as_view(), name='logout')と設定していますので、http://127.0.0.1:8000/logout/にアクセスした時点でまずLogoutViewがユーザーをログアウトさせます。次に、LOGOUT_REDIRECT_URLで指定したトップページにユーザーをリダイレクトさせたのです。

本当にログアウトできてるか確かめるために、Adminページにアクセスしてみてください。きっとログインを求められるでしょう。これは正常にログアウトできている証拠です。

ログインページhttp://127.0.0.1:8000/login/にアクセスしても、現状はlogin.htmlを作ってないのでエラーになります。では、login.htmlを作っていきましょう。

まずは、templates/appディレクトリの中に、login.htmlファイルを作成してください。中身は以下のようになります。

~/PhotoService/app/templates/app/login.html

{% extends 'app/base.html' %}

{% block content %}

<form method="post">{% csrf_token %}
    {{ form.as_p }}
    <input type="submit" value="ログイン">
</form>

{% endblock %}

ファイルを保存してhttp://127.0.0.1:8000/login/にアクセスすると、このようにフォームが表示されるはずです。

path('login/', auth_views.LoginView.as_view(template_name='app/login.html'), name='login')の設定により、login.htmlにはLoginViewがformを渡してくれるので、{{ form }}と書くことができます。しかも、このformは、AuthenticationFormという認証用に作られたフォームですので、ユーザー名とパスワードを入力させることができます。

ラベルなどをカスタマイズしたい場合は、{{ form.as_p }}ではなく以下のように書くこともできます。その際、inputタグのname属性はusernameとpasswordにしておく必要があります。また、{{ form.username }}のように書くことでも、name属性を持つinputタグを生成することが可能です。

~/PhotoService/app/templates/app/login.html

{% extends 'app/base.html' %}

{% block content %}

<form class="form-signin" method="post">{% csrf_token %}
    {% if form.errors %}
        <p>ユーザー名かパスワードが間違っています。もう一度入力してください。</p>
    {% endif %}
    <h2>ログイン</h2>
    <label>ユーザー名</label>
    <input name="username">
    <br>
    <label>パスワード</label>
    <input type="password" name="password">
    <br>
    <input type="submit" value="ログイン">
</form>

{% endblock %}

それでは、実際にフォームからユーザー名とパスワードを入力してログインしてみましょう。「ログイン」ボタンを押した時に問題なくログインできれば、LOGIN_REDIRECT_URLで設定したトップページにリダイレクトされます。ユーザー名やパスワードが間違っていて認証に失敗した場合は、login.htmlにエラーメッセージが表示されます。

ログインに成功しているかを確認するために、Adminページにアクセスしてみましょう。この時にログインを求められず、問題なく閲覧できればログインは成功しています。

ユーザーのログイン状態に合わせて表示を切り替える

最後に、ユーザーのログイン状態に合わせて画面表示を切り替えてみましょう。

base.htmlのheaderに以下のように追記してください。

~/PhotoService/app/templates/app/base.html

    <header>
        <div class="container">
            <h1><a href="{% url 'app:index' %}">写真投稿サイト</a></h1>
            <div class="header-menu">
                <a href="">投稿</a>
                ↓ 追加
                {% if request.user.is_authenticated %}
                    <a href="{% url 'app:users_detail' request.user.id %}">マイページ</a>
                    <a href="{% url 'app:logout' %}">ログアウト</a>
                {% else %}
                    <a href="{% url 'app:login' %}">ログイン</a>
                {% endif %}
            </div>
        </div>
    </header>

{% if request.user.is_authenticated %}というif文を追加しています。

views.pyの各関数で最後に実行されるrenderメソッドでは、requestを第一引数に取ってTemplate側にこのrequestを渡しています。このrequestの中にはサーバーに対してリクエストを送ってきたユーザー情報などが含まれています。そして、Templateでは、request.userとすることで、そのUserオブジェクトにアクセスすることができます。

また、Userオブジェクトがis_superuser等様々な属性を持っていることは既に紹介しましたが、is_authenticatedという属性も持っています。これは、ユーザーがログイン状態であればTrue、未ログイン状態であればFalseとなる属性です。

つまりこのif文では、アクセスしてきたユーザーがログイン状態の場合は「マイページ」と「ログアウト」へのリンクを表示し、ログイン状態でない場合にはログイン画面へのリンクを表示するようにしています。実際にログインとログアウトをしてみて、ヘッダーの表示が切り替わるか確認してみましょう。

・未ログイン時

・ログイン時

ログイン機能を実装できましたので、次のレッスンではユーザー登録機能をつくりましょう!

< PREV NEXT >
SHARE ! Tweet