2016年8月1日にDjango 1.10がリリースされました。このバージョンにアップデートしたところ、3点ほどはまったことがあったので忘備録として残しておきます。
authenticateでis_activeをチェック
django.contrib.auth.authenticate
メソッドで、is_active
がTrueかをチェックするようになりました。今までis_activeをチェックし、エラーコードを返していた場合、エラーコードが一致していない場合はテストケースなどの修正が必要になってきます。
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
# ユーザを作成します。
User.objects.all().delete()
username = 'foo'
password = 'password'
user = User.objects.create_user(username, 'foo@example.com', password)
# 認証されることを確認します。
user = authenticate(username=username, password=password)
type(user) # print <class 'django.contrib.auth.models.User'>
# is_activeをFalseにします。
user.is_active = False
user.save()
# 認証されない(Noneが返る)ことを確認します。
user = authenticate(username=username, password=password)
type(user) # print <class 'NoneType'>
ちなみにDjango 1.9ではis_activeに関わらず認証されていました。
...
# is_activeをFalseにします。
user.is_active = False
user.save()
# 認証されない(Noneが返る)ことを確認します。
user = authenticate(username=username, password=password)
type(user) # print <class 'django.contrib.auth.models.User'>
セッションからユーザ取得時のハッシュチェック
セッションからユーザを取得する際に、ハッシュ値のチェックが追加されました。具体的には、セッションに保持したハッシュ値と、ユーザモデルから計算したハッシュ値を比較しています。ハッシュ値の比較周り
デフォルトのユーザモデルを使っている場合、このハッシュ値の計算にはパスワードを使っています。そのためパスワードを更新するとハッシュ値が一致せず、AnonymousUserになってしまいます。それにより、パスワード変更後にログイン後のページにリダイレクトさせていた場合、ログイン前のページ(ログイン画面など)にリダイレクトしてしまいます。
from importlib import import_module
from django.conf import settings
from django.contrib.auth import update_session_auth_hash, get_user, SESSION_KEY, BACKEND_SESSION_KEY
from django.contrib.auth.models import User
from django.http import HttpRequest
# ユーザを作成します。
User.objects.all().delete()
username = 'myuser'
password = 'password'
user = User.objects.create_user(username, 'foo@example.com', password)
# セッションを作成します。
engine = import_module(settings.SESSION_ENGINE)
session = engine.SessionStore('123')
session[BACKEND_SESSION_KEY] = 'django.contrib.auth.backends.ModelBackend'
session[SESSION_KEY] = user.pk
# リクエストを作成します。
request = HttpRequest()
request.user = user
request.session = session
# ハッシュ値を計算してセッションに登録します。
update_session_auth_hash(request, user)
# セッションを使ってユーザを取得します。
get_user(request) # <User: myuser>
# パスワードを更新します。
user.password = 'BAR'
user.save()
# パスワード更新後、セッションからユーザを取得するとAnonymousUserになっています。
get_user(request) # <django.contrib.auth.models.AnonymousUser object at ...>
パスワード変更後、ユーザのセッションを継続するにはfrom django.contrib.auth.update_session_auth_hash
が使えます。
# Viewの中
user = form.save() # パスワード更新
update_session_auth_hash(request, user)
ちなみに、Django 1.9ではハッシュ値のチェックをしないため、パスワード変更してもセッションからユーザを取得できました。
...
user.password = 'BAR'
user.save()
get_user(request) # <User: myuser>
staticタグのエスケープ
これは元々の使い方が悪かったのが原因です。以下のようにstaticタグの中に「@」やスペースがあると、%xxを使ってエスケープされます。
{% load static %}
<img src="{% static 'foo.png' %}" srcset="{% static 'foo@2x.png 2x' %}" />
↓
<img src="/static/foo.png" srcset="/static/foo%402x.png%202x" />
拡張子(png)の後の「%202x」により、/static/foo%402x.png%202x
という画像がないため404を返すようになりました。
修正は、404が発生した原因の「 2x」をstaticタグの外に出してエスケープされないようにしました。
{% load static %}
<img src="{% static 'foo.png' %}" srcset="{% static 'foo@2x.png' %} 2x" />
↓
<img src="/static/foo.png" srcset="/static/foo%402x.png 2x" />
ちなみに、staticfiles
をINSTALLED_APPS
から除外した場合はエスケープされないみたいです。
INSTALLED_APPS = [
...
# 'django.contrib.staticfiles',
...
]
<img src="{% static 'foo.png' %}" srcset="{% static 'foo@2x.png 2x' %}" />
↓
<img src="/static/foo.png" srcset="/static/foo@2x.png 2x" />