Help us understand the problem. What is going on with this article?

【 Docker+Nginx+Django+RDS】WEBアプリができるまで④ログインログアウトをしよう

前置き

独学で、子供の成長アプリを作った時のことを、記録として残していきます。
間違っているところなどあれば、ご連絡お願いします。
 ①Djangoのようこそページへたどり着くまで
 ②NginxでDjangoのようこそページへたどり着くまで
 ③カスタムユーザーを作ってadminにたどり着く
 ④ログインログアウトをしよう <--ここです
 ⑤ユーザー登録(サインイン)機能を作ろう
 ⑥ユーザーごとのデータ登録できるようにする〜CRU編
 ⑦ユーザーごとのデータ登録できるようにする〜削除編
 ⑧画像ファイルのアップロード
 ⑨身長体重を記録する@一括削除機能つき
 ⑩成長曲線グラフを描いてみよう
 ⑪本番環境へデプロイ+色々手直し

Goal

ログインログオフ機能を作ろう

HTML(下準備)

Djangoのtemplateをアプリごとに分けるのが面倒になりそうなので、
テンプレートは一箇所に置くことにする。最終形は以下(細かいものは省略)。

.
├── docker-compose.yml
├── nginx
│   ├── conf
│   │   └── mysite_nginx.conf
│   └── uwsgi_params
├── src
│   ├── manage.py
│   ├── mysite
│   │   ├── settings.py
│   │   ├── urls.py
│   │   └── wsgi.py
│   ├── templates
│   │   ├── base.html
│   │   └── users
│   │       ├── login.html
│   │       ├── mypage.html
│   │       └── signup.html
│   └── users
│       ├── admin.py
│       ├── apps.py
│       ├── forms.py
│       ├── models.py
│       ├── urls.py
│       └── views.py
├── static
│   └── 略
└── web
    ├── Dockerfile
    └── requirements.txt

templateの場所を教えてあげるために、setting.pyを修正する。

setting.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',
            ],
        },
    },
]

CSSはbootstrapが手っ取り早くて良い。
bootstrapはdjango用のものがあるので、それを使う。

requirement.txt
Django==2.2.2
psycopg2==2.8.4
uwsgi==2.0.17
django-bootstrap4==1.1.1 #追加

settingはAPPSに追加するのと、Templateの指定にも追加する。

setting.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users.apps.UsersConfig',
    'bootstrap4', #追加
]
()
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',
            ],
            'builtins':[ #追加
                'bootstrap4.templatetags.bootstrap4',
            ],
        },
    },
]

HTML(本番)

ベースのHTML。bootstrapでレスポンシブ対応ぽくしている。

base.html
{% load static %}
{% bootstrap_css %}
{% bootstrap_javascript jquery='full' %}

<html>
    <head>
        <title>kids Growth</title>
        <link rel="stylesheet" href="{% static 'css/kidsGrowth.css' %}">
        {% block extra_js %}{% endblock %}
       </head>
    <body>
        <!-- Navigation -->
        <nav class="navbar navbar-expand-sm navbar-dark bg-dark mt-3 mb-3">
            <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarNav4" aria-controls="navbarNav4" aria-expanded="false" aria-label="Toggle navigation">
              <span class="sr-only">メニュー</span>  
              <span class="navbar-toggler-icon"></span>
            </button>
            <a class="navbar-brand" href="">Kids Growth</a>
            <div class="collapse navbar-collapse" id="navbarNav4">
                <ul class="navbar-nav">
                  <li class="nav-item">
                      <a class="nav-link" href="">メニュー1</a>
                  </li>
                  <li class="nav-item">
                      <a class="nav-link" href="">メニュー2</a>
                  </li>
                  <li class="nav-item">
                      <a class="nav-link" href="">メニュー3</a>
                  </li>
                  <li class="nav-item">
                      <a class="nav-link" href="{% url 'users:mypage'%}">マイページ</a>
                  </li>
                  {% if user.is_authenticated %}
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'users:logout'%}">ログアウト</a>
                    </li>
                  {% else %}
                    <li class="nav-item">
                        <a class="nav-link" href="{% url 'users:login'%}">ログイン</a>
                    </li>
                  {% endif %}
                </ul>
            </div>
        </nav>    

        <div class="content container">
          {% block content %}
          {% endblock %}
        </div>
    </body>
</html>

マイページ。(ユーザー名が出るだけ)
ログイン後に飛んでくるところとして指定。
{{ user }}はあとでViewから引き渡します。

users/mypage.html
{% extends 'base.html' %}

{% block content %}

<div class="col-md-12 col-lg-5">
    <h2>My Page</h2>

    <br>
    <br>

    <table class = "table">
      <tr>
        <td>ユーザー名</td>
        <td>{{ user }}</td>
      </tr>
    </table>


</div>

{% endblock content %}

formという名前でフォームを引き渡して、
bootstrap_formで綺麗に整形するだけ。
ユーザー登録のリンクを用意してますが、その機能は次で。

users/login.html
{% extends 'base.html' %}

{% block content %}

{{ form.media }}

<div class="col-md-12 col-lg-5">
    <h2>Login</h2>
        <form action="" method="POST">
                {{ form.non_field_errors }}
                {% bootstrap_form form %}
                <hr>
                <button type="submit" class="btn btn-success btn-lg btn-block" >ログイン</button>
                {% csrf_token %}
        </form>


        <a href="{% url 'users:signup' %}">ユーザー登録</a>

</div>
{% endblock %}

Form

FormとしてAuthenticationFormを継承することで、
ログイン機能を実装します。これだけ。便利。

users/form.py
class LoginForm(AuthenticationForm):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

View

ログインとログアウトは専用のViewがあるので継承する。
@login_requiredを指定した関数はログインした状態じゃないと呼ばれない。
ひとまずはログインしているユーザーの名前を取得して、templateに返すだけのお仕事。

ちなみにログインのクラスをloginにすると、このあとSignup関数を作るときに
規定のネーミングとバッティングするのかエラーが外れず、ややカッコ悪いネーミング。

users/view.py
from .forms import LoginForm
from django.shortcuts import render, redirect
from django.contrib.auth.views import LoginView, LogoutView
from django.contrib.auth.decorators import login_required

#ログイン
class login_mypage(LoginView):
    form_class = LoginForm
    template_name = 'users/login.html'

#ログアウト
class logout(LogoutView):
    template_name = 'users/login.html'

#マイページ
@login_required
def mypage(request):
    user_name = request.user
    params = {
        'user' : user_name,
    }
    return render(request, 'users/mypage.html',params)

urls

プロジェクトのurlsで、アプリごとのurlsを読み込む。

mysite/urls.py
from django.contrib import admin
from django.urls import path, include

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

アプリごとに定義したURLパターンごとにViewを決める。

users/urls.py
app_name = 'users'

urlpatterns = [
    path('', views.mypage, name='mypage'),
    path('mypage/', views.mypage, name='mypage'),
    path('login/', views.login_mypage.as_view(), name='login'),
    path('logout/', views.logout.as_view(), name='logout'),
]

Setting.py

以下を追加する。ログインするときのURLはここだよ〜
ログインしたらここに最初にアクセスしてね〜
ログアウトしたらなんの画面を見せるよ〜
ということを決める。

setting.py
LOGIN_URL = 'users:login'
LOGIN_REDIRECT_URL = 'users:mypage'
LOGOUT_REDIRECT_URL = 'users:login'

出来上がり

ログイン画面と、ログインした後の画面。

スクリーンショット 2020-02-11 20.45.05.png
スクリーンショット 2020-02-11 20.45.49.png

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした