Django - Qiita上のtutorial appを概観し、機能をプラスする（２）

## はじめに
前回[Django - Qiita上のtutorial appを概観し、機能をプラスする（１）](https://qiita.com/emo157/items/c645b33e3a36ccd1d6bc)の続きです。

Djangoにはログイン機能について標準でユーザー認証(login)機能がついていますが、ユーザー登録(signup)機能は備わっていません。

今回はユーザー認証機能とユーザー登録機能とそれら画面を実装していきます。
ログアウト画面も作ってよいのですが、今回はログイン画面に遷移させることで対応します。


参照記事は以下です。
[Django2 でユーザー認証（ログイン認証）を実装するチュートリアル -2- サインアップとログイン・ログアウト](https://it-engineer-lab.com/archives/544)

以下前回の機能も付与してgitにあげたものです。
https://github.com/Rio157/crud-image-accounts.git


## 完成型
 ![b2b6f6da-fefe-4019-bbc4-56761af38df4-1920x913r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515538/9558b025-0d6c-5f21-4465-bd6bc54b178a.png)
 ログイン画面
 
![97b64710-5e1c-4585-ae49-0bfcad2c5fa6-1920x900r.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/515538/6868a5b6-3ef6-d174-b92e-ba9c3ad2a259.png)
ユーザー登録画面



## ユーザー認証機能（login）とその画面
まずはurlの指定から。


```settings.py(project下)
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL='/'
# login後、logout後にリダイレクトするページを指定します。
# ここでは一覧画面に飛ぶようにします。
```

```urls.py(project下)
    path('accounts/', include('django.contrib.auth.urls')), 
    # urlspatternsに追加。これでdjangoの標準でついている認証機能が有効になりました。
```
次にtemplatesをつくっていきます。

```_base.html(templates/app下)
    {% block customcss %}
    {% endblock customcss %}
    <!--headタグに追加。_base.htmlを継承したときにcssファイルを読み込むことができます。-->
    <!--また、bodyタグ内で管理サイト、ログアウトと並んで、-->
    <li class="nav-item">
        <a class="nav-link" href="{% url 'accounts:signup'%}">ユーザー登録</a>
    </li>
    <!--を追加。また、ログアウトでは-->
　　<li class="nav-item">
　       <a class="nav-link" href="{% url 'logout'%}">ログアウト</a>
  　</li>
  <!--{% url 'admin:logout'%}→{% url 'logout' %}とします。管理者用の認証機能によるログアウト画面に遷移させないためです。-->
```

```login.html(templates下にregistrationフォルダ作成、registration下)
{% extends 'app/_base.html' %}
{% load static %}

{% block customcss %}
<link rel='stylesheet' type='text/css' href="{% static 'app/css/style.css' %}">
{% endblock customcss %}

{% block content %}
<section class="common-form">
    {% if form.errors %}
    <p class="error-msg">Your username and password didn't match. Please try again.</p>
    {% endif %}

    {% if next %}
    {% if user.is_authenticated %}
    <p class="error-msg">Your account doesn't have access to this page. To proceed,
        please login with an account that has access.</p>
    {% endif %}
    {% endif %}

    <form class="form-signin" method="POST" action="{% url 'login'%}">{% csrf_token %}
        <h1 class="h3 mb-3 font-weight-normal">Please login</h1>
        <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>
        <div class="checkbox mb-3">
        </div>
        <button class="btn btn-lg btn-primary btn-block" type="submit">Log in</button>
        <p class="mt-5 mb-3 text-muted">&copy; 2017-2020</p>
    </form>
</section>
{% endblock %}
```
{% extends 'ファイル名' %}で継承。cssを読み込むときは{% load static %}と追加します。

先にユーザー登録画面も含めたcssファイルを作成しておきます。

```style.css(app/static/app/css下)
    div, p, ul, ol, li, dl, dt, dd, h1, h2, h3, h4, h5, h6 label, input, textarea, select, button {
    margin: 0;
    padding: 0;
    color: #555555;
    font-size: 1rem;
    line-height: 1.8;
    box-sizing: border-box;
  }
  
  
  h1{
      font-size: 2rem;
      padding-top: 20px;
      padding-bottom: 15px;
  }
  
  section {
    margin: 0 0 8px;
  }
  
  .container {
    margin: 0 auto;
    width: 100%;
    max-width: 800px;
  }
  
  .content {
    padding: 0 8px;
  }
  .common-form label {
    display: block;
  }
  
  .common-form p {
    margin-bottom: 8px;
  }
  
  .common-form input, .common-form textarea {
    padding: 4px;
    width: 100%;
    margin-bottom: 8px;
  }
  
  .common-form select {
    padding: 4px;
    margin-bottom: 8px;
  }
  
  .common-form .submit {
    margin-top: 8px;
    margin-bottom: 8px;
    padding: 8px 36px;
    border: none;
    color: #ffffff;
    text-align: center;
    text-decoration: none;
    display: inline-block;
    background-color: #4CAF50;
    border-radius: 2px;
  }
  
  .common-form .delete {
    background-color: #f44336;
  }
  
  .form-signin {
    width: 100%;
    max-width: 315px;
    padding: 15px;
    margin: auto;
  }
  .form-signin .checkbox {
    font-weight: 400;
  }
  
  .form-signin h1{
    margin-top: 50px;
  }
```
ログイン画面と同様に、ユーザー登録画面でもstyle.cssを読み込みます。
ちなみにこのcssはbootstrapのサンプルページ画面からとってきたものです。
## ユーザー登録機能（signup）とその画面
まずは新たにアプリを立ち上げます。
terminalで以下を実行。

```
django-admin startapp accounts
python3 manage.py makemigrations
python3 manage.py migrate
```
プロジェクトにおいて新しいアプリを認証してもらいます。

```settings.py(project下)
  INSTALLED_APPS = [...,
                    'accounts.apps.AccountsConfig',
                   ]
```

```urls.py(project下)
    path('accounts/', include('accounts.urls')), 
    #urlpatternsに追加。
    #これでaccountsアプリのurlがブラウザで探せるようになりました。
```
accountsアプリに戻ります。
accountsアプリ下にurls.pyを新たに作成します。

```urls.py(accounts下)
from django.urls import path

from . import views

# set the application namespace
# https://docs.djangoproject.com/en/2.0/intro/tutorial03/
app_name = 'accounts'

urlpatterns = [
    # ex: /accounts/signup/
    path('signup/', views.SignUpView.as_view(), name='signup'),
]
```

```views.py(accounts下)
from django.contrib.auth.forms import UserCreationForm
from django.urls import reverse_lazy
from django.views import generic
 
 
class SignUpView(generic.CreateView):
    form_class = UserCreationForm
    success_url = reverse_lazy('login')
    template_name = 'accounts/signup.html'
    #ユーザー登録のフォーム自体はDjangoに標準で備わっているので、
    #UserCreationFormを指定、reverse_lazyでログイン画面に遷移すると追加、templatesのディレクトリを指定します。
```
accounts下にtemplates/accountsディレクトリを作成後、

```signup.html
{% extends 'app/_base.html' %}
{% load static %}

{% block customcss %}
<link rel='stylesheet' type='text/css' href="{% static 'app/css/style.css' %}">
{% endblock customcss %}

{% block content %}
<h1>Sign up</h1>
<section class="common-form">
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit" class="submit">Sign up</button>
    </form>
</section>
{% endblock %}
```
ログイン画面と同様style.cssを読み込みます。

## 振り返り
お疲れさまでした。

今回は最小限の機能と見栄えを実装しました。諸々改善できる点（ログイン失敗時の画面はcssできれいにしていない、signupにある注意事項が嘘になっているとか）はありますが、ひとまず作業フロー全体を捉えることはできました。

個人的には昨年末あたりにやっていた作業をまとめたので、いい復習になりました。その点、書き漏れなどあるかもしれませんが、悪しからず。