Djangoのユーザ認証まとめ
追記: 使用しているDjangoのバージョンをはじめ、いくつか古くなってきている点があります。DjangoCongress JP 2018で認証に関する発表を行ったのですが、ブログ記事も用意しています。そちらを参照してください
追記終わり
Djangoでユーザ認証をしたかったので調べてみると,DjangoではUserモデルがはじめから用意されているらしい.そこでDjangoの提供する機能をそのまま使ってログイン・ログアウトを実装してみた.
さらに調べてみるとpython-social-auth
というライブラリを使えば、TwitterやFacebook,Google、Github等のアカウントを使ったOAuth認証が簡単に実装できるみたいなのでそれも試しておく.
- https://github.com/c-bata/django-auth-example
- こちらはソースコードが古くなっているため、Django CongressJP 2018のソースコード置き場に変更しました。
- この記事で解説しているソースコードをもし読みたい場合は、 source-year-2015 ブランチ に残しています。
環境はPython 3.4、Django 1.7を使用しています.
目次
Djangoの提供する機能でログイン・ログアウト
準備
まずプロジェクトを作成する。
$ django-admin.py startproject user_auth $ python manage.py migrate $ python manage.py createsuperuser : スーパユーザを作成 $ python manage.py runserver
ブラウザで http://127.0.0.1:8000/admin を開いてログイン出来るか確認.
うまく行ったなら,次はアプリケーションを作成する.認証用に独立したアプリケーションを用意したほうが良さそうなので,accountsというアプリケーションを作成した.
$ python manage.py startapp accounts
user_auth/settings.py
を開いてINSTALLED_APPSにアプリケーションを追加.ついでに言語とタイムゾーンの設定もしておく.
INSTALLED_APPS = ( : 'accounts', ) LANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo'
ログイン・ログアウトページの実装
djangoにはログイン・ログアウト用のviewがデフォルトで用意されているので,それをurls.py
に指定するだけでいい.
urlpatterns = patterns('', url(r'^admin/', include(admin.site.urls)), url(r'^login/$', 'django.contrib.auth.views.login', {'template_name': 'accounts/login.html'}), url(r'^logout/$', 'django.contrib.auth.views.logout', {'template_name': 'accounts/logged_out.html'}), )
ちなみに,registration
フォルダを作成して,その中にhtmlファイルを置く場合には,,{'template_name': 'path/to/template'}
が省略できる.
今回はaccounts/templates/accounts/login.html
に配置したため上記のように設定する必要がある.
accounts/templates/accounts/login.html
{% extends "base.html" %} {% block content %} {% if form.errors %} <p>Your username and password didn't match. Please try again.</p> {% endif %} <form method="post" action="{% url 'django.contrib.auth.views.login' %}"> {% csrf_token %} <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> <input type="submit" value="login" /> <input type="hidden" name="next" value="{{ next }}" /> </form> {% endblock %}
accounts/templates/accounts/logged_out.html
{% extends "base.html" %} {% block title %}ログアウト{% endblock title %} {% block content %} <h3 class="page-header">ログアウトしました</h3> {% endblock content %}
このままだと,ログインフォームにユーザ名とパスワードを入力してログインしても,Page not foundが表示される.デフォルトでは,ログインに成功するとhttp://127.0.0.1:8000/accounts/profile/
このようなURLにリダイレクトされるので,そこにユーザのプロフィールページ等を置く.
もし他の場所にリダイレクトさせたい場合は,settings.py
の中でLOGIN_REDIRECT_URL
を指定する.
ログイン状態の確認
login_required
デコレータを使えば,ページの閲覧をログイン済みのユーザだけに制限することが出来る.
ログインしてないならsettings.py
のLOGIN_URL
に設定したパスに?next=requesst.path
を付けて飛ばす
from django.contrib.auth.decorators import login_required @login_required def my_view(request): ...
login_required
デコレータを使わない場合は以下の様にする.
from django.http import HttpResponseRedirect def my_view(request): if not request.user.is_authenticated(): return HttpResponseRedirect('/login/?next=%s' % request.path)
ここまでの内容は↓のcommitのdiffを見るのが分かりやすいかと思います.
Implement login and logout · c-bata/django-auth-example@dbea95f · GitHub
Userモデルをカスタマイズ
参考
上の記事によるとDjango1.5以降ならAbstractUserモデルを実装すればいいらしい.
accounts/models.py
from django.contrib.auth.models import AbstractUser from django.db import models class CustomUser(AbstractUser): image_url = models.URLField('画像URL', blank=True)
accounts/admin.py
from django.contrib import admin from accounts.models import CustomUser admin.site.register(CustomUser)
user_auth/settings.py
AUTH_USER_MODEL = 'accounts.CustomUser'
実行
$ python manage.py makemigration $ python manage.py migrate $ python manage.py runserver
管理サイトにログインする.ログインできなくなっていた.コンソールで確認する.
>>> from accounts.models import CustomUser >>> CustomUser.objects.all() [] >>> from django.contrib.auth.models import User >>> User.objects.all() Traceback (most recent call last): File "<input>", line 1, in <module> File "/Users/masashi/.virtualenvs/itukalist/lib/python3.4/site-packages/django/db/models/manager.py", line 230, in __get__ self.model._meta.object_name, self.model._meta.swapped AttributeError: Manager isn't available; User has been swapped for 'accounts.CustomUser'
どうやらもう一度createsuperuserする必要があるらしい.
$ python manage.py createsuperuser : adminアカウントを作成
ログインしてみると今度はうまく行った! adminの中身を見てみると画像URLの項目ができていることが確認できる.
他の確認方法
>>> from django.contrib.auth import get_user_model >>> get_user_model() <class 'account.models.CustomUser'>
補足
今回は試してないが,AbstractBaseUserを使うと自前で1から定義できるらしい. もし必要なら↓の記事が役に立ちそう.
Django 1.5のカスタムユーザーモデルとdjango-registration その2 - Misc Notes
Userモデルをキーとして新しいモデルを定義する
ブログやタスク管理アプリなどを作るなら,User以外にもモデルを定義しなければならない. ブログ記事やタスク等はUserモデルに紐付けたいため,Userモデルの外部キーとして定義する.
accounts
アプリケーションは認証機能だけにしたいので,新たなアプリケーションを作ってそこにタスクモデルを作成する.
$ python manage.py startapp api
user_auth/settings.py
INSTALLED_APPS = ( : 'api', )
api/models.py
Two Scoops of Django: Best Practices for Django 1.8 によると、settings.AUTH_USER_MODEL
を使うらしい。 get_user_model()
を使ってはいけない。importがloopするらしい。
from django.db import models from user_auth import settings class Task(models.Model): user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='tasks') summary = models.CharField('タスク', max_length=128) complete = models.BooleanField('状態', default=False) comment = models.CharField('コメント', max_length=512, blank=True) done_date = models.DateField('完了日', null=True, blank=True) def __str__(self): return str(self.id) + ': ' + self.summary
api/admin.py
from django.contrib import admin from api.models import Task admin.site.register(Task)
これらを追記したらマイグレーションして実行してみる.
$ python manage.py makemigration $ python manage.py migrate $ python manage.py runserver
管理システムにログインすると,Taskモデルが追加されている. 試しにUserモデルとTaskモデルのレコードを追加してみて確認したほうがいいかもしれない.
DjangoでTwitter,Facebook,Google,Github認証
Python Social Authを使ってThird-party authenticationを行う.
- GitHub - omab/python-social-auth: Social auth made simple
- Welcome to Python Social Auth’s documentation! — Python Social Auth documentation
- 2014 - Art+Logic: Custom Software Development
準備
次はWebブラウザから確認するためのアプリケーションなのでweb
という名前で作成する.
$ python manage.py startapp web
user_auth/settings.py
INSTALLED_APPS = ( : 'web', )
web/views.py
from django.shortcuts import render_to_response from django.template import RequestContext def home(request): context = RequestContext(request, {'user': request.user}) return render_to_response('web/home.html', context_instance=context)
web/templates/web/home.html
templatesディレクトリは無ければ作成.
{% extends "base.html" %} {% block title %}ホーム{% endblock title %} {% block content %} <div> <h1>ホーム</h1> <p> {% if user and not user.is_anonymous %} Hello {{ user.get_full_name|default:user.username }}! {% else %} ログインして下さい. {% endif %} </p> </div> {% endblock content %}
base.html
は,accounts/templates/
からプロジェクトルート直下のtemplatesディレクトリ内に移動.
user_auth/urls.py
以下を追加
url(r'^$', 'web.views.home', name='home'),
ここまで追加したら動かしてみる. ログインしてからホームに行くと,ユーザ名が表示される.
python-social-auth
$ pip install python-social-auth
user_auth/settings.py
AUTHENTICATION_BACKENDSの中にFacebookとかGithubとか追加していけば良いけど, 登録が面倒なので今回はTwitterだけ.
INSTALLED_APPS = ( : 'social.apps.django_app.default', ) TEMPLATE_CONTEXT_PROCESSORS = ( 'django.contrib.auth.context_processors.auth', 'django.core.context_processors.debug', 'django.core.context_processors.i18n', 'django.core.context_processors.media', 'django.core.context_processors.static', 'django.core.context_processors.tz', 'django.contrib.messages.context_processors.messages', 'social.apps.django_app.context_processors.backends', 'social.apps.django_app.context_processors.login_redirect', ) AUTHENTICATION_BACKENDS = ( 'social.backends.twitter.TwitterOAuth', 'django.contrib.auth.backends.ModelBackend', ) LOGIN_REDIRECT_URL = '/' SOCIAL_AUTH_TWITTER_KEY = 'Your Twitter Key' SOCIAL_AUTH_TWITTER_SECRET = 'Your Twitter Secret'
補足
ログインはhref="{% url 'social:begin' 'twitter' %}"
のようにすれば開始できるが,href="{% url 'social:begin' 'twitter' %}?next={{ next }}"
のようにすれば,ログイン後next
で指定したパスにリダイレクトさせる事ができる.nextを指定しなかった場合settings.py
のLOGIN_REDIRECT_URL
にリダイレクトする.
web/templates/web/home.html
{% extends "base.html" %} {% block title %}ホーム{% endblock title %} {% block content %} <div> <h1>ホーム</h1> <p> <ul> {% if user and not user.is_anonymous %} <li> <a>Hello {{ user.get_full_name|default:user.username }}!</a> </li> <li> <a href="{% url 'auth:logout' %}?next={{ request.path }}">Logout</a> </li> {% else %} <li> <a href="{% url 'social:begin' 'twitter' %}?next={{ request.path }}">Login with Twitter</a> </li> {% endif %} </ul> </p> </div> {% endblock content %}
web/views.py
from django.shortcuts import render_to_response from django.template import RequestContext def home(request): context = RequestContext(request, {'request': request, 'user': request.user}) return render_to_response('web/home.html', context_instance=context)
追記できたら、migrateしてrunserver. トップページからTwitterログインのボタンを押すと,認証が出来る. 認証後,管理サイトから確認すると自分のTwitterアカウントがCustomUserの中に追加されている.
終了!
終わりに
アカウント作成の手間を考えるとpython-social-authを使ってthird-party authenticationの方に利点がある.これからWebアプリ書く時はpython-social-authガンガン使っていこう.
↓の本にベストプラクティスが詰まっててとても良かった。
Two Scoops of Django: Best Practices for Django 1.8
- 作者: Audrey Roy Greenfeld
- 出版社/メーカー: Lightning Source Inc
- 発売日: 2015/05/15
- メディア: ペーパーバック
- この商品を含むブログを見る