Djangoのセッション管理について


普段django使ってアプリ開発とかしてるわけですが、実は中身がどうなってるのかあまり知らずに使ってます。
今日はなんとなくセッション管理について調べてみました。

セッションを使うには?

セッションを有効にする方法

MIDDLEWARE_CLASSESに、 'django.contrib.sessions.middleware.SessionMiddleware' を入れる
INSTALLED_APPSに 'django.contrib.sessions' を入れる

とはいえ、どちらもデフォルトで入っている。

セッション使わない場合は外したほうがパフォーマンスがよいのでしょう。

セッションエンジン

セッションはデフォルトではdbに保存される
テーブル名はdjango_session
モデルはdjango.contrib.sessions.models.Session

settings.SESSION_ENGINEを設定することによって、memcachedにしたり、ファイルに保存したり、クッキーベースセッションにしたりできる

memcachedのほうがパフォーマンスがよいのでしょう。ただ永続化はされない、と。

例えばログイン状態をどのようにセッションで管理しているか


django-registrationを使ってログイン、ログアウトするだけのdjangoプロジェクトを作ってセッションに何が入るか見てみました。


まずサイトにアクセスすると、Set-Cookieでsessionidが付与されます。



django_sessionテーブルにはこの時点でデータが保存されてます。

mysql> select * from django_session;
+----------------------------------+-------------------------------------------------------------------+---------------------+
| session_key                      | session_data                                                      | expire_date         |
+----------------------------------+-------------------------------------------------------------------+---------------------+
| d9a0e9ccb8a1ae2ceb0306599f2a9602 | OTk0NDljMmQ2MzI4ZjZlNzkwMDY4YzU5MjJhOGU2ZWQxMzllZTZmNzqAAn1xAS4=
 | 2013-03-23 08:02:37 |
+----------------------------------+-------------------------------------------------------------------+---------------------+
1 row in set (0.00 sec)

session_keyはSet-Cookieでブラウザに渡された値ですね


session_dataには何が入ってるのでしょうか。

Sessionモデルにget_decodedメソッドが用意されてるので、これで中身を見れます。

>>> from django.contrib.sessions.models import Session
>>> Session.objects.get(session_key='d9a0e9ccb8a1ae2ceb0306599f2a9602').get_decoded()
{}


空の辞書でした。


ログインページに飛んでみると、その時点でsesseion_dataが書きかわりました。

mysql> select * from django_session;
+----------------------------------+----------------------------------------------------------------------------------------------------+---------------------+
| session_key                      | session_data                                                                                       | expire_date         |
+----------------------------------+----------------------------------------------------------------------------------------------------+---------------------+
| d9a0e9ccb8a1ae2ceb0306599f2a9602 | ZTg1MjgwZjEwYjYwNTI1M2MxNWNmNDNlMjYyMDQ5MzJmYWMxZjBkYzqAAn1xAVUKdGVzdGNvb2tp
ZXECVQZ3b3JrZWRxA3Mu
 | 2013-03-23 08:08:11 |
+----------------------------------+----------------------------------------------------------------------------------------------------+---------------------+
>>> Session.objects.get(session_key='d9a0e9ccb8a1ae2ceb0306599f2a9602').get_decoded()
{'testcookie': 'worked'}

testcookieというのがセットされてました。djangodjango.contrib.auth.views.login関数がセットしたんですね。


ログインしてみます


302レスポンスが返されて、Set-Cookieがついてるのでsession_keyが変わりました。

mysql> select * from django_session;
+----------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+
| session_key                      | session_data                                                                                                                                                                                | expire_date         |
+----------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+
| c88514f077edd10eecbbfdc822657f9c | YWY1MDVlZDAyYmQ2MGRkNGQyZGQ4YjhkNDZiNzBkYjc2YzJlM2Y0YjqAAn1xAShVEl9hdXRoX3Vz
ZXJfYmFja2VuZHECVSlkamFuZ28uY29udHJpYi5hdXRoLmJhY2tlbmRzLk1vZGVsQmFja2VuZHED
VQ1fYXV0aF91c2VyX2lkcQSKAQF1Lg==
 | 2013-03-23 08:14:38 |
+----------------------------------+---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------+---------------------+


ログイン後の状態

>>> Session.objects.get(session_key='c88514f077edd10eecbbfdc822657f9c').get_decoded()
{'_auth_user_id': 1L, '_auth_user_backend': 'django.contrib.auth.backends.ModelBackend'}

ユーザーIDとbackendを保持してますね
これでcookieでsession_keyが渡されれば、どのユーザーからのアクセスかわかる、すなわちログイン状態ってわけですね


ログアウトしてみます。


ログアウト後のリダイレクト先でまたSet-Cookieでsessionidが変わります

mysql> select * from django_session;
+----------------------------------+-------------------------------------------------------------------+---------------------+
| session_key                      | session_data                                                      | expire_date         |
+----------------------------------+-------------------------------------------------------------------+---------------------+
| c5e527f0f14901ab8ed0a35d3943729f | OTk0NDljMmQ2MzI4ZjZlNzkwMDY4YzU5MjJhOGU2ZWQxMzllZTZmNzqAAn1xAS4=
 | 2013-03-23 08:21:40 |
+----------------------------------+-------------------------------------------------------------------+---------------------+
1 row in set (0.00 sec)


session_dataはまた空になりました

>>> from django.contrib.sessions.models import Session
>>> Session.objects.get(session_key='c5e527f0f14901ab8ed0a35d3943729f').get_decoded()
{}

ログイン状態のときセッションにセットされた値はどこで使ってるのか、みてみると

AuthenticationMiddlewareでrequest.userをセットしてるので、その辺で使ってそうです。

class AuthenticationMiddleware(object):
    def process_request(self, request):
        assert hasattr(request, 'session'), "The Django authentication middleware requires session middleware to be installed. Edit your MIDDLEWARE_CLASSES setting to insert 'django.contrib.sessions.middleware.SessionMiddleware'."

        request.user = SimpleLazyObject(lambda: get_user(request))

get_userを追っかけてみると
django.contrib.auth.get_userで使ってました。

SESSION_KEYが'_auth_user_id'、BACKEND_SESSION_KEYが'_auth_user_backend'ですね

def get_user(request):
    from django.contrib.auth.models import AnonymousUser
    try:
        user_id = request.session[SESSION_KEY]
        backend_path = request.session[BACKEND_SESSION_KEY]
        backend = load_backend(backend_path)
        user = backend.get_user(user_id) or AnonymousUser()
    except KeyError:
        user = AnonymousUser()
    return user


backendにはModelBackend以外になにがあるのでしょうか
調べてみるとRemoteUserBackendというのがありました。
これは今度調べてみよう。