naritoブログ

【お知らせ】
・コメントで質問等をしたが返事が返ってこない場合、私はそれを見落としています。
その場合は再度コメントをするかメールをしてください(toritoritorina@gmail.com)。

・近いうちに新しいブログが作成されます。わーお!

Djangoでログイン画面を自作する

約148日前 2018年4月28日17:23
プログラミング関連
Django Python

概要


Djangoで、会員登録機能を自作するの1つです。
前回で、メールアドレスをユーザー名として使うカスタムユーザーを定義しました。

今回はログイン画面を実装していきます。

見た目


ログインしていない場合は、トップページの上にログインページへのリンクが表示されます。
ログインしていないときは、ログインページへのリンクが表示される

ログインページは、今のところシンプルです。
ログインページの様子

パスワードを間違ったりすると、エラーが表示されますね。
ログインに失敗したら、エラーもちゃんとでます

無事にログインすると、デフォルトではトップページへリダイレクトします。ログインしているので、ログアウト用のリンクができていますね。
ログインするとトップページに移動し、ログアウトリンクが作られます

ソースコードと解説



settings.py


ログイン画面と、直接ログイン画面に移動してログインした後に、どこにリダイレクトするかの設定を書きます。
reverse関数に渡せるような形で、文字列として定義することができます。
LOGIN_URL = 'register:login'
LOGIN_REDIRECT_URL = 'register:top'


プロジェクトのurls.py


もちろん、プロジェクトのurls.pyで読み込ませることも必要です。
from django.contrib import admin
from django.urls import path, include

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


models.pyやadmin.pyは、前回と変わりがありません。

アプリケーションのurls.py


トップページ、ログイン、ログアウトですね。
from django.urls import path
from . import views

app_name = 'register'

urlpatterns = [
    path('', views.Top.as_view(), name='top'),
    path('login/', views.Login.as_view(), name='login'),
    path('logout/', views.Logout.as_view(), name='logout'),
]



forms.py


ログイン用のフォームを、Bootstrap4対応させます。
今回はラベルを使わず、placeholder内にラベルを表示させる方法を使います。
カスタムユーザーでもデフォルトユーザーでも、問わず使えます。
from django.contrib.auth.forms import (
    AuthenticationForm
)


class LoginForm(AuthenticationForm):
    """ログインフォーム"""

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        for field in self.fields.values():
            field.widget.attrs['class'] = 'form-control'
            field.widget.attrs['placeholder'] = field.label  # placeholderにフィールドのラベルを入れる




views.py


トップページは単純なTemplateViewで、ログインとログアウトはDjangoに用意されたビューを使うのが一番簡単です。
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth.views import (
    LoginView, LogoutView
)
from django.views import generic
from .forms import LoginForm


class Top(generic.TemplateView):
    template_name = 'register/top.html'


class Login(LoginView):
    """ログインページ"""
    form_class = LoginForm
    template_name = 'register/login.html'


class Logout(LoginRequiredMixin, LogoutView):
    """ログアウトページ"""
    template_name = 'register/top.html'



base.html


{% if user.is_authenticated %}を使って、ログイン済みならログアウト用のリンクを、そうでなければログイン用のリンクを表示させています。
<!doctype html>
<html lang="ja">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css"
          integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">

    <title>会員登録サンプル</title>
</head>
<body>
    <!-- ナビバー -->
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
      <a class="navbar-brand" href="{% url 'register:top' %}">ホームページ名</a>
      <button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
        <span class="navbar-toggler-icon"></span>
      </button>

      <div class="collapse navbar-collapse" id="navbarSupportedContent">
        <ul class="navbar-nav mr-auto">
          {% if user.is_authenticated %}
          <li>
            <a class="nav-item nav-link" href="{% url 'register:logout' %}">ログアウト</a>
          </li>
          {% else %}
          <li class="nav-item">
            <a class="nav-item nav-link" href="{% url 'register:login' %}">
            ようこそ、ゲスト!ログインはこちら
          </a>
          </li>
          {% endif %}
        </ul>
      </div>
    </nav>

    <!-- メインコンテント -->
    <div class="container mt-3">
        {% block content %}{% endblock %}
    </div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.3.1.slim.min.js"
            integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo"
            crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.0/umd/popper.min.js"
            integrity="sha384-cs/chFZiN24E4KMATLdqdvsezGxaGsi4hLGOzlXwp5UZB1LY//20VyM2taTB4QvJ"
            crossorigin="anonymous"></script>
    <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/js/bootstrap.min.js"
            integrity="sha384-uefMccjFJAIv6A+rW+L4AHf99KvxDjWSu1z9VI8SKNVmz4sk7buKt/6v9KI65qnm"
            crossorigin="anonymous"></script>
</body>
</html>



top.html


便宜上用意しただけのページなので、何も言うことはありません。
{% extends "register/base.html" %}
{% block content %}
<p>ここに、好きな内容を入れる。</p>
{% endblock %}



login.html


<div class="col-md-6 offset-md-3">のように書くと、良い具合に入力部分を中央寄せできます。それ以外は、よくあるフォームですね。
{% extends "register/base.html" %}
{% block content %}
<form action="" method="POST">
    <div class="col-md-6 offset-md-3">
        <div class="card">
            <div class="card-body">
                {{ form.non_field_errors }}
                {% for field in form %}
                    {{ field }}
                    {{ field.errors }}
                    <hr>
                {% endfor %}
                <button type="submit" class="btn btn-success btn-lg btn-block" >ログイン</button>
                <input type="hidden" name="next" value="{{ next }}" />
                {% csrf_token %}
            </div>
        </div>
    </div>
</form>
{% endblock %}

名無し 約105日前 2018年6月10日12:23 返信する
こんにちは、いつも楽しく見させていただいています。

なりと先生はアプリケーションのurls.pyの中で、app_name = 'register'と書いていますが、これはプロジェクトのurls.pyでurl('', include('register.urls'), name_space='register')として名前空間を設定することと同じ意味があるのでしょうか?
なりと 約105日前 2018年6月10日13:15
path('', include('app.urls', namespace='app')),
という書き方は、Django2.0で使えなくなったためです。
namespace引数の代わりに、urls.pyでapp_nameとするようになりました。
細かい話をすると別の書き方もあるのですが、ややこしいので使ったことがありません。

urls.pyでapp_nameを指定していれば、それに加えてnamesplace引数も指定できます。
これはインスタンスの名前空間という、アプリケーションの名前とは別のものなのですが、今のところは使ったことがないです。
名無し 約105日前 2018年6月10日15:26 返信する
詳細な解説ありがとうございます。まさかnamespaceという書き方が出来なくなっていたとは知りませんでした。
今度から自分で書くときも、app_nameを使うようにします。