【Django】DjangoによるWeb開発プロジェクトPart2 - ログインページを作成する所まで -

## はじめに

前回の内容から大分、間が空いてしまいましたがDjangoによるWeb開発を進めていきたいと思います。
今回はDjangoにログイン認証の画面を作成する所までを進めていきたいと思います。

## アプリケーションを作成する

DjangoではTOPディレクトリに作成したプロジェクトの配下となるアプリケーションを作成する事で、独立した操作を行えるアプリケーションを作成する事ができます。プロジェクトはそのアプリケーションを一つにまとめたものです。
サブモジュールとして`sample_project/作成するアプリケーション名`とすることもできますが、今回はTOPディレクトリに置くことにします。

アプリケーションを作成するには、 manage.py と同じディレクトリに入って、以下のコマンドを実行します。

```bash
% python manage.py startapp polls
```

pollsのディレクトリ構成は以下のようになります。

```bash
% ls -al polls
total 21
drwxr-xr-x  3 himrock922  himrock922   9 11月  4 18:18 .
drwxr-xr-x  6 himrock922  himrock922  12 11月  4 18:18 ..
-rw-r--r--  1 himrock922  himrock922   0 11月  4 18:18 __init__.py
-rw-r--r--  1 himrock922  himrock922  63 11月  4 18:18 admin.py
-rw-r--r--  1 himrock922  himrock922  91 11月  4 18:18 apps.py
drwxr-xr-x  2 himrock922  himrock922   3 11月  4 18:18 migrations
-rw-r--r--  1 himrock922  himrock922  57 11月  4 18:18 models.py
-rw-r--r--  1 himrock922  himrock922  60 11月  4 18:18 tests.py
-rw-r--r--  1 himrock922  himrock922  63 11月  4 18:18 views.py
```

## viewの作成

Djangoで最も単純なビューページを書いていきます。

```python:polls/views.py
from django.http import HttpResponse

def index(request):
    return HttpResponse("Hello, world. You're at the polls index.")
```

単純な文字出力を行うviewを呼ぶために、URLに対応づけしてあげる必要があります。
そのためには、`URLconf` が必要となります。

```python:polls/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]
```

views.pyのindexメソッドを呼び出し、パス名を`index`として、紐付けさせます。
次に、ルートのURLConfにaccounts.urlsモジュールを読み込ませます。

```python
from django.contrib import admin
from django.urls import include, path

urlpatterns = [
    path('polls/', include('polls.urls')),
    path('admin/', admin.site.urls),
]
```

`include()`関数は他のURLConfへの参照をする事ができます。

以上で簡単なURLのPathを作成する事ができました。
この内容に沿った形でログイン認証を行うページを作ろうと思ったのですが、実は既にDjangoには認証システムが用意されています。

## Djangoの認証システムを使ってログイン認証システムを作る

[Djangoの認証システムを使用する](https://docs.djangoproject.com/ja/2.0/topics/auth/default/)

こちらにきめ細かな仕様が載っているのですが、情報量が多すぎるので読むのが少し大変でした汗。ひとまず、認証を実現する機能として必要なメソッドやモデルが既に用意されている`django.contrib.auth`があるのでstartappで作る必要がない模様です。
これだけだと、分かりにくかったのでもう一つ参考文献を。
[Django Login/Logout Tutorial (Part 1)](https://wsvincent.com/django-user-authentication-tutorial-login-and-logout/)

認証用のviewは実装されているが対応するテンプレートはサポートしていないため、各自で実装すること、またサインアップのメソッドは用意されていないため、こちらも各自で書く必要があります。いずれにせよ、startappでアプリケーションを作成する必要がないので、上の`Django Login/Logout Tutorial (Part 1)`を参考にログイン認証を作っていきます。

```bash
% rm -rf polls
```

### Auth App

Djangoプロジェクトを構築した時に自動的に`auth` アプリケーションがインストールされています。

```python:settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```

authアプリケーションを使うために、urls.pyにaccounts配下のパスとauth.urlsのパターンを紐付けさせます。

```python:urls.py
from django.contrib import admin
from django.urls import include, path

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

authアプリケーションをincludeすると、以下の認証ビューが提供されます。

```bash
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']
```

ついでにこの際時刻設定と日本語設定、およびsqlite3からpostgresqlを使う様に設定します。

```bash
% sudo pkg install postgresql10-server-10.5 postgresql10-contrib-10.5

% sudo vim /etc/login.conf
postgres:\
	:lang=en_US.UTF-8:\
	:setenv=LC_COLLATE=C:\
	:tc=default:
% sudo vim /etc/rc.conf
postgresql_class="postgres"
postgresql_enable="YES"

% sudo service postgresql initdb
% sudo service postgresql start

$ sudo su - postgres

$ createdb mydb
$ createuser -P
$ psql
$ GRANT ALL PRIVILEGES ON DATABASE mydb TO myuser;
```

```bash
% pip install psycopg2
```

```python:settings.py

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'mydb',
        'USER': 'myuser',
        'PASSWORD': 'mypass',
        'HOST': 'localhost',
        'PORT': '',
    }
}

(中略)
LANGUAGE_CODE = 'ja'

TIME_ZONE = 'Asia/Tokyo'
```

## ログインページを作成する

ログインページを作ります。authはデフォルトでregistration配下のテンプレートフォルダを読み込みます。registrationディレクトリを作成し、loginテンプレートとしてlogin.htmlを作成します。

```bash
% mkdir -p templates/registration
```

```html:templates/registration/login.html
<h2>Login</h2>
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Login</button>
</form>
```

`{% csrf_token %}でCSRFトークンを発行でき、`{{ form.as_p }}`でログインフォームを作成できます。

続いてDjangoが`templates`ディレクトリを見つけられるように、settings.pyを修正します。

また、ログイン成功後のリダイレクトページを指定するために`LOGIN_REDIRECT_URL`を追加します。

```python:settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
LOGIN_REDIRECT_URL = '/'
```

ユーザーを作成後、Djangoを再起動すると http://127.0.0.1:8080/accounts/login においてログインページが表示されます。

![スクリーンショット 2018-11-18 22.49.35.png](https://qiita-image-store.s3.amazonaws.com/0/144991/ec377561-e072-a0cc-6dfc-b27c38e8a167.png)

ログインできるようにユーザー作成を行いましょう。

```bash
% python3.7 manage.py migrate                                           (git)-[feature/add_login]
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying sessions.0001_initial... OK
% python manage.py createsuperuser
ユーザー名 (leave blank to use '*********'): 
メールアドレス: ********
Password: 
Password (again): 
Superuser created successfully.
```

以上で、作成したユーザーでログインする事ができるのですが、ログイン成功後のリダイレクトページをまだ作っていないため、レスポンスは404が返ってきます。

![スクリーンショット 2018-11-18 22.54.20.png](https://qiita-image-store.s3.amazonaws.com/0/144991/76d106c6-7084-abb5-bddf-3fa191345305.png)

次にindexのページを作ってあげましょう。

## indexページの作成

indexのページと全ページ共通のrenderページとしてapplication.htmlを作成します。

```html:templates/application.html
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8">
    <title>{% block title %}Django Auth Tutorial{% endblock %}</title>
  </head>
  <body>
    <main>
      {% block content %}
      {% endblock %}
    </main>
  </body>
</html>
```

```html:templates/index.html
{% extends 'application.html' %}

{% block title %}Home{% endblock %}

{% block content %}
{% if user.is_authenticated %}
  Hi {{ user.username }}!
{% else %}
  <p>You are not logged in</p>
  <a href="{% url 'login' %}">login</a>
{% endif %}
{% endblock %}
```

また、それに伴いlogin.htmlをapplication.htmlを読み込むように修正します。

```html:templates/registration/login.html
{% extends 'application.html' %}

{% block title %}Login{% endblock %}

{% block content %}
<h2>Login</h2>
<form method="post">
  {% csrf_token %}
  {{ form.as_p }}
  <button type="submit">Login</button>
</form>
{% endblock %}
```

次にurls.pyを修正し、/のurlパターンの際に読み込むTemplateを指定してあげます。

```python:urls.py
from django.contrib import admin
from django.urls import include, path
from django.views.generic.base import TemplateView

urlpatterns = [
    path('admin/', admin.site.urls),
    path('accounts/', include('django.contrib.auth.urls')),
    path('', TemplateView.as_view(template_name='index.html'), name='index'),
]
```

最後にLOGIN_REDIRECT_URLを`/`から`index`に変更します。

```settings.py
LOGIN_REDIRECT_URL = 'index'
```

以上でログイン遷移のページまで作成することができました。

![login.gif](https://qiita-image-store.s3.amazonaws.com/0/144991/ae986295-bae8-9ab4-86e7-9f5ec77a5a6e.gif)


## あとがき

もうちょいペースアップします。

## シリーズ

[Part 1](https://qiita.com/himrock922/items/1daf1c5c6eedfcb7f661) ← [Part 2](https://qiita.com/himrock922/items/bd020a0d313233556c97) → [Part 3](https://qiita.com/himrock922/items/facef344ad20f307720e)


## 参考文献

* [はじめての Django アプリ作成、その 1](https://docs.djangoproject.com/ja/2.1/intro/tutorial01/)
* [https://docs.djangoproject.com/ja/2.0/topics/auth/default/](https://docs.djangoproject.com/ja/2.0/topics/auth/default/)
* [Django Login/Logout Tutorial (Part 1)](https://wsvincent.com/django-user-authentication-tutorial-login-and-logout/)