投稿日: | 最終更新日:
Djangoのログイン・ユーザ登録(カスタムユーザモデル)
Djangoが標準で持つUserモデルを継承してカスタマイズします。
ユーザ登録ページ
前回、Userモデルをカスタマイズし、以下のような変更を加えました。
- idの代わりにuuidを使います。
- usernameの代わりにemailを使います。
- nick_nameを追加します。
- emailを必須かつユニークに変更します。
- usernameは消します。
- first_nameとlast_nameを消しします。
通常のユーザモデルでは、ログイン・ユーザ登録にusernameを使いましたが、これを削除しました。usernameの代わりにemailをユーザ名として利用します。今回は、この改造したユーザモデルを使ったユーザ登録ページを作ります。
URL設計
トップページ(/index) | ドメイン直下のページです。ログイン前はページタイトルのみですが、ログインすると、ヘッダーナビゲーションにログアウトのリンクを表示します。 |
---|---|
ログイン(/login) | ユーザ名とパスワードを入力してログインします。ログインが完了すると、ログイン状態になってトップページへ遷移します。 |
ログアウト(/logout) | ログアウト状態にしてトップページへ遷移します。 |
ユーザ登録(/regist) | ユーザ登録をします。登録が完了すると、ログイン状態にしてトップページへ遷移します。 |
CSS設計
bootstrap4を使います。css、jsファイルの設置場所はプロジェクトディレクトリ直下のstaticディレクトリです。
- pj1/
- pj1/
- static/
- css/
- js/
- users/
- manage.py
settings.pyに、静的ファイルの設置場所を設定します。
STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), ) STATIC_URL = '/static/'
仕様
①トップページです。「ドメイン/」にアクセスします。画面右上にログインページへのリンクがあります。
ログインページです。「ドメイン/login」にアクセスします。新規登録ページへのリンクもあります。
ログインページからログインした場合は「ドメイン/」(トップページ)へ遷移します。画面左上の”ログイン”が”ログアウト”になります。
新規登録ページです。「ドメイン/regist」にアクセスします。
ログアウトを押すと、ログアウト処理後トップページへリダイレクトします。
準備
Vagrantでゲスト環境(仮想環境)を作ります。
①Userモデルをカスタマイズしています。
→DjangoのUserモデルカスタマイズ(UUIDを使う)
環境
OS | CentOS 7.1.1503 |
---|---|
pyenv | 1.1.3-5-g7dae197 |
Anaconda | 3-4.3.0 |
MariaDB | 5.5.52-1.el7 |
Apache | 2.4.6 |
mod_wsgi | 4.5.14 |
Django | 1.11.3 |
ユーザ登録概要
Userモデルの継承してカスタマイズします。usernameの代わりにemailを使うようにしています。
①以下のようなDjangoアプリ構成を作ります。プロジェクト名が「pj1」で、アプリケーション名が「users」です。プロジェクトの設定は済ませたものとします。赤字のファイルは、今回変更する部分です。
- pj1/
- pj1/
- __init__.py
- settings.py
- urls.py
- wsgi.py
- users/
- __init__.py
- admin.py
- apps.py
- urls.py
- views.py
- models.py
- templates/
- base.html
- index.html
- login.html
- regist.html
- manage.py
models.py
①usersディレクトリ配下のmodels.pyを編集します。内容は前回と同じです。
→DjangoのUserモデルカスタマイズ(UUIDを使う)
urls.py
①pj1ディレクトリのurls.pyを以下のように編集します。
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url('admin/', admin.site.urls), url(r'^', include('users.urls', namespace = 'users')) ]
②usersディレクトリのurls.pyを以下のように編集します。
from django.conf.urls import include, url from django.contrib.auth import views as auth_views from . import views app_name = 'users' urlpatterns = [ url(r'^$', views.index, name='index'), # ログイン、ログアウト url(r'^login/$', views.login, name='login'), url(r'^logout/$', views.logout, name='logout'), url(r'^regist/$', views.regist, name='regist'), url(r'^regist_save/$', views.regist_save, name='regist_save'), ]
forms.py
①usersディレクトリのforms.pyを以下のように編集します。
from django import forms from django.contrib.auth.forms import UserCreationForm from django.contrib.auth.forms import AuthenticationForm from users.models import User #from django.contrib.auth import get_user_model User = get_user_model() from django.contrib.auth.forms import PasswordChangeForm class LoginForm(AuthenticationForm): def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['username'].widget.attrs['class'] = 'form-control' self.fields['username'].widget.attrs['placeholder'] = 'メールアドレス' self.fields['password'].widget.attrs['class'] = 'form-control' self.fields['password'].widget.attrs['placeholder'] = 'パスワード' class RegisterForm(UserCreationForm): # 入力を必須にするため、required=Trueで上書き email = forms.EmailField(required=True) nick_name = forms.CharField(required=True) class Meta: model = User fields = ( "email", "password1", "password2", "nick_name", ) def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) self.fields['nick_name'].widget.attrs['class'] = 'form-control' self.fields['nick_name'].widget.attrs['placeholder'] = 'お名前' self.fields['email'].widget.attrs['class'] = 'form-control' self.fields['email'].widget.attrs['placeholder'] = 'メールアドレス' self.fields['password1'].widget.attrs['class'] = 'form-control' self.fields['password1'].widget.attrs['placeholder'] = 'パスワード' self.fields['password2'].widget.attrs['class'] = 'form-control' self.fields['password2'].widget.attrs['placeholder'] = 'パスワード(確認)'
解説
from users.models import User
models.pyで定義したカスタムUserモデルをインポートします。
from django.contrib.auth import get_user_model User = get_user_model()
Djangoの定義済みを使用する場合は、上記のようにインポートします。
Manager isn't available; 'auth.User' has been swapped for 'users.User'
カスタムユーザモデルを使用する場合、「from django.contrib.auth.models import User」をインポートしてユーザ登録すると、上記のようなエラーが表示されてしまいます。
# 入力を必須にするため、required=Trueで上書き email = forms.EmailField(required=True) nick_name = forms.CharField(required=True) class Meta: model = User fields = ( "email", "password1", "password2", "nick_name", )
もともとUserCreationFormでは、MetaのmodelにUserを指定しています。また、「fields = (“username”,)」のように不要なフィールドが指定がされています。今回は、設定しなおし「email、nick_name、password1、password2」も使用するようにしています。更に、「email = forms.EmailField(required=True)」で必須項目にしました。
views.py
①usersディレクトリのviews.pyを以下のように編集します。
from django.contrib.auth.forms import AuthenticationForm from django.contrib.auth.decorators import login_required from django.http import HttpResponse from django.shortcuts import render, redirect, get_object_or_404 from django.views.decorators.http import require_POST from .forms import ( LoginForm, RegisterForm, UserPasswordChangeForm ) from django.contrib.auth.models import User from django.urls import reverse_lazy from django.contrib.auth import views as auth_views def index(request): context = { 'users':request.user, } return render(request, 'index.html', context) def login(request): context = { 'template_name': 'login.html', 'authentication_form': LoginForm } return auth_views.login(request, **context) def logout(request): context = { 'template_name': 'index.html', } return auth_views.logout(request, **context) def regist(request): form = RegisterForm(request.POST or None) context = { 'form':form, } return render(request, 'regist.html', context) @require_POST def regist_save(request): form = RegisterForm(request.POST) if form.is_valid(): form.save() return redirect('users:index') context = { 'form': form, } return render(request, 'regist.html', context)
解説
@login_required
デコレータです。「ログインしたユーザのみ見せる」という意味です。ログインしていない場合は、ログインページへリダイレクトします。
def regist(request): form = RegisterForm(request.POST or None) context = { 'form':form, } return render(request, 'regist.html', context)
ユーザ新規登録画面を表示します。
@require_POST def regist_save(request): form = RegisterForm(request.POST) if form.is_valid(): form.save() return redirect('users:index') context = { 'form': form, } return render(request, 'regist.html', context)
「RegisterForm」を使って、ユーザ登録画面に入力した情報を保存します。登録できた場合は「index」ページへリダイレクトします。
テンプレート
{% load static %} <!DOCTYPE html> <html lang="ja"> <head> <meta charset="utf-8"> <meta http-equiv="X-UA-Compatible" content="IE=edge"> <meta name="viewport" content="width=device-width, initial-scale=1"> <link rel="shortcut icon" href="{% static 'main/img/favicon.ico' %}"> <!-- The above 3 meta tags *must* come first in the head; any other head content must come *after* these tags --> <title>ユーザ登録</title> <!-- Bootstrap --> <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet"> <link href="{% static 'css/bootstrap-reboot.min.css' %}" rel="stylesheet"> <link href="{% static 'css/bootstrap-grid.min.css' %}" rel="stylesheet"> <script src="{% static 'js/jquery-3.2.1.min.js' %}"></script> <script src="{% static 'js/bootstrap.min.js' %}"></script> <style> .my-form { width: 640px; margin: 0 auto; } @media screen and (max-width: 768px) { .my-form { width: 100%; } } .errorlist li { list-style-type: none; } .errorlist { color: red; margin-left: 0; padding-left: 0; } </style> </head> <body> <nav class="navbar navbar-toggleable-md navbar-inverse bg-inverse"> <button class="navbar-toggler navbar-toggler-right" type="button" data-toggle="collapse" data-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation"> <span class="navbar-toggler-icon"></span> </button> <a class="navbar-brand" href="#">App1</a> <div class="collapse navbar-collapse" id="navbarNav"> <ul class="navbar-nav"> <li class="nav-item active"> <a class="nav-link" href="{% url 'users:index' %}">Home</a> </li> <li class="nav-item"> <a class="nav-link" href="{% url 'users:profile' %}"">プロフィール</a> </li> </ul> </ul> <ul class="nav navbar-nav ml-auto"> <li class="nav-item"> {% if user.is_authenticated %} <a class="nav-link" href="{% url 'users:logout' %}">ログアウト</a> {% else %} <a class="nav-link" href="{% url 'users:login' %}">ログイン</a> {% endif %} </li> </ul> </div> </nav> {% block content %} {% endblock %} <!-- Bootstrap --> <link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet"> <link href="{% static 'css/bootstrap-reboot.min.css' %}" rel="stylesheet"> <link href="{% static 'css/bootstrap-grid.min.css' %}" rel="stylesheet"> <!-- js --> <script src="{% static 'js/bootstrap.min.js' %}"></script> <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries --> <!-- WARNING: Respond.js doesn't work if you view the page via file:// --> <!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]--> </body> </html>
「Base.html」は、すべてのページに表示するウィジェット(ページヘッダー)を定義します。
{% extends "base.html" %} {% block content %} <div class="container-fluid" style="margin-top: 60px;"> <div class="row"> <div class="col-md-6 offset-md-3"> <p>インデックスです</p> </div><!-- end col-md-6 offset-md-3 --> </div><!-- end row --> </div><!-- end container-fluid--> {% endblock %}
「index.html」は、トップページの表示です。
{% extends 'base.html' %} {% block content %} <div class="container-fluid" style="margin-top: 60px;"> <div class="row"> <div class="col-md-6 offset-md-3"> <div class="card"> <div class="card-header"> ログイン</div> <div class="card-body"> <div class="card-block"> <form class="my-form" action="{% url 'users:login' %}" method="POST"> {% csrf_token %} <div class="row"> <div class="form-group col-md-11"> <label for="id_email">メールアドレス</label> {{ form.username }} {{ form.username.errors }} <p class="help-block mb-0">{{ form.username.help_text }}</p> </div> <div class="form-group col-md-11 pb-3"> <label for="id_email">パスワード</label> {{ form.password }} {{ form.password.errors }} </div> <div class="form-group col-md-11"> <input type='hidden' name='next' value='{{ next }}' /> <button type="submit" class="btn btn-block btn-outline-primary"> ログイン</button> </div> </div> </form> <div class="card-text"> <p class="text-right mb-0"><a class="nav-link" href="#">パスワードを忘れた方</a></p> <p class="text-right mb-0"><a class="nav-link" href="{% url 'users:regist' %}">新規登録会員</a></p> </div><!-- end card-text --> </div> </div><!-- end card-body--> </div><!-- end card--> </div> </div><!-- end container-fluid --> </div><!-- end container-fluid--> {% endblock %}
「login.html」は、ログインページの表示です。
{% extends "base.html" %} {% block content %} <div class="container-fluid" style="margin-top: 60px;"> <div class="row"> <div class="col-md-6 offset-md-3"> <div class="card"> <div class="card-header">会員登録用URL送信</div> <div class="card-body"> <div class="card-block"> <form class="my-form" action="" method="POST"> {% csrf_token %} <div class="row"> <div class="form-group col-md-11"> <label for="id_nick_name">お名前</label> {{ form.nick_name }} {{ form.nick_name.errors }} </div> <div class="form-group col-md-11 my-0"> <label for="id_email">メールアドレス</label> {{ form.email }} {{ form.email.errors }} <p class="help-block">{{ form.email.help_text }}</p> </div> <div class="form-group col-md-11"> <label for="id_password1">パスワード</label> {{ form.password1 }} {{ form.password1.errors }} </div> <div class="form-group col-md-11"> <label for="id_password2">パスワード(確認用)</label> {{ form.password2 }} {{ form.password2.errors }} <p class="help-block">{{ form.password2.help_text }}</p> </div> <div class="form-group col-md-11"> <input type='hidden' name='next' value='{{ next }}' /> <button type="submit" class="btn btn-block btn-outline-primary">メールを送信する</button> </div> </div> </form> </div><!-- end card-block --> </div><!-- end card-body --> </div><!-- end card --> </div><!-- end col-md-6 offset-md-3 --> </div><!-- end row --> </div><!-- end container-fluid --> {% endblock %}
「regist.html」は、新規登録ページの表示です。
- Python 114
- 制作 54
- RaspberryPi 41
- Django 40
- WordPress 40
- Linux 27
- VPS 22
- JavaScript 21
- PHP 20
- HTML・CSS 19
- AWS 16
- 仮想環境 15
- レスポンシブデザイン 13
- マイコン 11
- WEB全般 11
- 動画製作 9
- Webサービス 8
- 統合開発環境 8
- 機械学習 8
- PyCharm 7
- jQuery 7
- AfterEffects 7
- 起業・設立 7
- Django REST framework 6
- C# 6
- デザイン 6
- SEO 6
- pydata 6
- Visual Studio 5
- 数学 5
- 携帯サイト 5
- heroku 5
- Mac 5
- illustrator 5
- node.js 5
- Anaconda 5
- Nginx 4
- Jupyter Notebook 4
- インフラ 4
- Google Colaboratory 4
- symfony 4
- Webスクレイピング 3
- photoshop 3
- Go言語 3
- PC 3
- ツール 3
- Docker 3
- facebook 3
- 作業効率化 3
- データベース 3
- Cloud9 3
- コマンド 2
- micro:bit 2
- Kali Linux 2
- Webサーバー 2
- MariaDB 2
- ドローン 2
- コンテナ 2
- DaVinci Resolve 2
- ネットワーク 2
- Java 2
- movie 2
- PCDJ 2
- 音楽 2
- XSERVER 2
- Ansible 1
- Vue.js 1
- JSON 1
- Bootstrap 1
- バージョン管理システム 1
- SSL 1
- S3 1
- ムームードメイン 1
- ネットワーク 1
- アニメーション 1
- D3.js 1
- Rhino 1
- アニメ 1
- git 1
- windows 1
- アクセス解析 1
- スマートフォン 1
- アフィリエイトノウハウ 1
- 知識 1
- TypeScript 1
- 役立つ本・書籍 1
- データサイエンス 1
- ESP32 1
- AI 1
- ownCloud 1
- API 1