追記: 使用している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
- メディア: ペーパーバック
- この商品を含むブログを見る