認証・認可は、Djangoに限らず、Webフレームワークを使い出してまず外せない機能。
Djangoの公式マニュアルを見てみると、機能としてデフォルトで持っていて、それを用途に応じて拡張していく方針とのこと。 Users, Groupモデル、パスワードをハッシュ化して保持して保持するといった提供されている。
1回やり方を確認しておけば何にでも応用できそうなのででBootstrap3を利用し、ログイン画面を表示してみる。
環境
環境としては、こちら。
項目 | バージョン |
---|---|
Python | 3.5.1 |
Django | 1.9.5 |
jQuery | v2.2.2 |
Bootstrap | v3.3.6 |
Djangoの認証
Djangoで認証・認可の機能は、django.contrib.auth
で使用できる。これはプロジェクトを生成するときに元から含まれている。プロジェクト作成後、マイグレーション(python manage.py migrate
)とすると認証・認可用のテーブルが作成される。
つくってみる
はじめにプロジェクトから作成する。
radish
はただのプロジェクトの例なので、適宜変更してください。
$ django-admin startproject radish $ python manage.py migrate
次にaccounts
とdashboard
アプリを作成する。
accounts
には認証・認可機能を含めで、dashboard
には認証された後のリダイレクト先のアプリを含める想定。
$ python manage.py startapp accounts $ python manage.py startapp dashboard
アプリを作ったら、忘れずsettings.py
のINSTALLED_APPS
に追記しておく。これを忘れると、後で出てくるテンンプレートが存在しないというエラーが発生するので、忘れずに。
radish/radish/settings.py
INSTALLED_APPS = [ 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', # local 'accounts.apps.AccountsConfig', 'dashboard.apps.DashboardConfig', ]
Bootstrapを配置する
JavascriptやCSSのライブラリは、radish/static
フォルダ内に入れておく。するとsettings.py
の下記の記載に沿って、参照可能となる。
radish/radish/settings.py
# Static files (CSS, JavaScript, Images) # https://docs.djangoproject.com/en/1.9/howto/static-files/ STATIC_URL = '/static/' STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), )
jQueryやらBootstrapやらをインストールする。それぞれダウンロードするのが面倒であったので、Bowerで入れる。
radish/static/
$ bower init ... (対話型でbower.jsonを作成) ... $ bower install jquery -S $ bower install bootstrap -S
トップ画面、ログイン画面、ログイン後画面(ダッシュボード画面)を作る
各画面共通で使うNavbarなどはbase.html
に記述し、他の画面はそれを継承する。またそういう共通のHTMLはradish/templates
配下に入れておく。しかし、このパスはDjangoが見つけてくれないので、またsettigs.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', ], }, }, ]
それではbase.html
。{% load staticfiles %}と書くと、下のように
{% static xxx%}でjsやcssを利用できる。{% if user.is_authenticated %}~{% else %}
で条件分岐しているところは、認証されている場合のみ有効にするメニュー表示である。また {% block container %}~{% endblock %}
は、base.html
を継承した方のHTMLが埋め込まれる箇所となっている。
{% load staticfiles %} <!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"> <tile>{% block title %} Radish | {{ site_name }}{% endblock %}</tile> <!-- CSS --> <link href="{% static 'bootstrap/dist/css/bootstrap.min.css' %}" rel="stylesheet" /> <link href="{% static 'bootstrap/dist/css/bootstrap-theme.min.css' %}" rel="stylesheet" /> <link href="{% static 'font-awesome/css/font-awesome.css' %}" rel="stylesheet" /> </head> <body> <nav class="navbar navbar-inverse navbar-fixed-top"> <div class="container"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <sapn class="icon-bar"></sapn> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="#">Radish</a> </div><!-- /.navbar-header --> <div id="navbar" class="navbar-collapse collapse"> {% if user.is_authenticated %} <ul class="nav navbar-nav navbar-right"> <li> <a href="#">Dashboard</a> </li> <li> <a href="#">Settings</a> </li> </ul> {% else %} <form class="navbar-form navbar-right"> <button type="button" class="btn btn-primary" onclick="location.href="{% url 'login' %}";"> Login</button> </form> {% endif %} </div><!-- /.navbar-collapse --> </div><!-- /.container --> </nav> {% block container %} {% endblock %} <hr/> <footer> <p>© hermesian</p> </footer> <!-- JavaScript --> <script src="{% static 'jquery/dist/jquery.min.js' %}"></script> <script src="{% static 'bootstrap/dist/js/bootstrap.min.js' %}"></script> </body> </html>
次にトップ画面home.html
は下記。
これはBootstrapのjumbotronの例をそのまま利用。
{% extends 'base.html' %} {% load staticfiles %} {% block container %} <!-- Main jumbotron for a primary marketing message or call to action --> <div class="jumbotron"> <div class="container"> <h1>Radish</h1> <p> "Radish is a dashboard for collecting visualizing report." </p> </div> </div> <div class="container"> <!-- Example row of columns --> <div class="row"> <div class="col-md-4"> <h2>Heading</h2> <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p> <p><a class="btn btn-default" href="#" role="button">View details »</a></p> </div> <div class="col-md-4"> <h2>Heading</h2> <p>Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui. </p> <p><a class="btn btn-default" href="#" role="button">View details »</a></p> </div> <div class="col-md-4"> <h2>Heading</h2> <p>Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.</p> <p><a class="btn btn-default" href="#" role="button">View details »</a></p> </div> </div> </div> {% endblock %}
home.html
のログインボタンで遷移する先は、accounts/tempalte/accounts/login.html
。<form>
タグの下に{% csrf_token %}
とあるのは、DjangoのCSRF対策機能である。
{% extends 'base.html' %} {% load staticfiles %} {% block container %} <div class="container"> <div class="page-header"> <h3> Login </h3> </div> <div class="row"> <div class="col-md-4 col-md-offset-4"> <div class="panel panel-default"> <div class="panel-body"> <form accept-charset="utf-8" role="form" action="{% url 'login' %}" method="post"> {% csrf_token %} <fieldset> <div class="form-group"> <input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus> </div> <div class="form-group"> <input type="password" id="password" name="password" class="form-control" placeholder="Password" required> </div> {% if login_failed %} <p class="text-danger">Sorry, that login was invalid. Please try again.</p> {% endif %} <input class="btn btn-lg btn-success btn-block" type="submit" value="Log in"> </fieldset> </form> </div> </div> </div> </div> {% endblock %}
最後に認証後のdashboard画面dashboard/template/dashboard.html
。といっても今回は空っぽ。
{% extends 'base.html' %}
ビューとルーティングを作る
accountsアプリ
radish/accounts/views.py
何かしらで認証されっぱなしの状態にならないように、logout
を最初に呼んで初期化してる。
import logging from django.shortcuts import render_to_response, HttpResponseRedirect from django.template import RequestContext from django.contrib.auth import authenticate, login, logout logger = logging.getLogger(__name__) def login_view(request): #強制的にログアウト logout(request) username = password = '' login_failed = False if request.POST: username = request.POST['username'].replace(' ', '').lower() password = request.POST['password'] user = authenticate(username=username, password=password) if user is not None: if user.is_active: login(request, user) return HttpResponseRedirect('/dashboard') else: login_failed = True return render_to_response('accounts/login.html', {'login_failed': login_failed}, context_instance=RequestContext(request))
上で作ったビューをwebブラウザから/accounts/login
でアクセスできるよう設定する。
radish/accounts/urls.py
from django.conf.urls import url from .views import login_view urlpatterns = [ url(r'^login/$', login_view, name='login'), ]
dashboardアプリ
radish/dashboard/views.py
@login_required
をつけて、認証後のみアクセス可能なようにしている。
import logging from django.shortcuts import render from django.contrib.auth.decorators import login_required @login_required def index(request): return render(request, 'dashboard/index.html')
radish/dashboard/urls.py
from django.conf.urls import url from .views import index urlpatterns = [ url(r'^$', index, name='dashboard'), ]
動作確認
ユーザを作成する。今回はスーパーユーザを造り、そのままそのアカウントでログインしてみる。
$ python manage.py createsuperuser ... (対話的に作成) ...
動作確認として、開発サーバを起動$ python manage.py runserver
、webブラウザからhttp://localhost:8000/
にアクセスして、先ほど登録したスーパーユーザでログインし、NavbarにDashboard
, Settings
というメニューが出ていることを確認する。