Djangoのセッションバックエンドを調べる(file, db)
セッションキーがどうも取得できないと思っていたら、DBじゃなくてfileバックエンドになっていたことがあった。
参考
基本的には公式ドキュメントを読むのが一番だと思う。
- https://docs.djangoproject.com/en/1.8/topics/http/sessions/
- http://docs.python.jp/3/library/tempfile.html
- http://docs.python.jp/3.3/library/shutil.html
準備
今回試した結果のリポジトリはこれ https://github.com/altnight/djsession
pythonのインストールなど
だいたいこういうことをした
virtualenv env -p `which python3` . ./env/bin/activate python --version # Python 3.4.3 pip install django # Successfully installed django-1.8.3 pip install ipython django-admin.py startproject djsession django-admin.py startapp app
初期化
runserver の前に migate or syncdb
が必要。今回はなんとなく admin を使いたかったので syncdb にしたけれど、どうやら syncdb
は django 1.9
で廃止されるらしい。
$ python manage.py syncdb /path/to/djsession/virtualenv/lib/python3.4/site-packages/django/core/management/commands/syncdb.py:24: RemovedInDjango19Warning: The syncdb command will be removed in Django 1.9 warnings.warn("The syncdb command will be removed in Django 1.9", RemovedInDjango19Warning) Operations to perform: Synchronize unmigrated apps: messages, staticfiles Apply all migrations: contenttypes, sessions, auth, admin Synchronizing apps without migrations: Creating tables... Running deferred SQL... Installing custom SQL... Running migrations: Rendering model states... DONE Applying contenttypes.0001_initial... OK Applying auth.0001_initial... OK Applying admin.0001_initial... OK Applying contenttypes.0002_remove_content_type_name... OK Applying auth.0002_alter_permission_name_max_length... OK Applying auth.0003_alter_user_email_max_length... OK Applying auth.0004_alter_user_username_opts... OK Applying auth.0005_alter_user_last_login_null... OK Applying auth.0006_require_contenttypes_0002... OK Applying sessions.0001_initial... OK You have installed Django's auth system, and don't have any superusers defined. Would you like to create one now? (yes/no): yes Username (leave blank to use 'altnight'): admin Email address: admin@admintestsite.com Password: Password (again): Superuser created successfully.
アプリの準備
- installed app 追加
- viewの作成
- url 登録
- template 作成
どこにソース上の記述があるか
だいたい django.contrib.sessions
以下をみればよさそう。今回はセッションで使っている値がどこに保存されているかを調べたかったので、 backends
であっていた。
file
django.contrib.sessions.backends.file
db
django.contrib.sessions.backends.db
どこで指定するか
設定ファイルなのだけど。 django-admin.py startproject djsession
で実行した時に生成された設定ファイルには記述がなかった。
Django の設定値の初期値は django.conf.global_settings
にある。それをみると以下の記述があったので、DBだった。
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
file
モジュールを文字列で指定しているので、 file
だった。
SESSION_ENGINE = 'django.contrib.sessions.backends.file' SESSION_FILE_PATH = os.path.join(BASE_DIR, 'session_file')
SESSION_FILE_PATH
はディレクトリである必要がある。最初存在しないてきとうなパスにしたら以下のエラーが出た。
ImproperlyConfigured at / The session storage path '/path/to/djsession/djsession/session_file' doesn't exist. Please set your SESSION_FILE_PATH setting to an existing directory in which Django can store session data.
db
SESSION_ENGINE = 'django.contrib.sessions.backends.db'
他
backends 以下のモジュールは以下ななので、他の方法もある。
- (init)
- (base)
- cache
- acched_db
- signed_cookies
どこに保存されているか
file
SESSION_FILE_PATH
で指定したディレクトリ以下に保存されている。
$ ls session_file sessionidnbd3c1righob86pm4pdjuvvo8u45bud4 $cat session_file/sessionidnbd3c1righob86pm4pdjuvvo8u45bud4 Njk2ZWY1MDRmMzcwZjA0NGNlZDQzNzc5YTdjMWY3NjIwMmVhMjBkNDp7InVzZXJfaWQiOjF9%
db
django_session
テーブルに保存されている。
$ sqlite3 --line db.sqlite3 sqlite> .table auth_group auth_user_user_permissions auth_group_permissions django_admin_log auth_permission django_content_type auth_user django_migrations auth_user_groups django_session sqlite> select * from django_session; session_key = 5lonf9nkfy1167jvlwpygz1s0dmdi6ih session_data = MTdlZTUxZWNkMzc4NDNhODZmZGUzOGViNTdlZTc3YTA5N2Y5YjlkODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c2VyX2hhc2giOiJiN2Y0MzM0ODcxOTI2ZjQ1NGJhZTk0MjdiMmU4NzNjZWUwODEwZWZlIiwidXNlcl9pZCI6MX0= expire_date = 2015-08-01 10:00:19.658846
どういう内容になっているか
file
実装をみると base64
でエンコードされているので、デコードでOKだった。
$ ipython In [1]: import base64 In [2]: base64.b64decode('Njk2ZWY1MDRmMzcwZjA0NGNlZDQzNzc5YTdjMWY3NjIwMmVhMjBkNDp7InVzZXJfaWQiOjF9') Out[2]: b'696ef504f370f044ced43779a7c1f76202ea20d4:{"user_id":1}'
key:value
という形になっている。これの key は cookie の sessionid
の値。
db
こちらも base64
でデコードすればOK。
In [3]: base64.b64decode('MTdlZTUxZWNkMzc4NDNhODZmZGUzOGViNTdlZTc3YTA5N2Y5YjlkODp7Il9hdXRoX3VzZXJfaWQiOiIxIiwiX2F1dGhfdXNlcl9iYWNrZW5kIjoiZGphbmdvLmNvbnRyaWIuYXV0aC5iYWNrZW5kcy5Nb2RlbEJhY2tlbmQiLCJfYXV0aF91c 2VyX2hhc2giOiJiN2Y0MzM0ODcxOTI2ZjQ1NGJhZTk0MjdiMmU4NzNjZWUwODEwZWZlIiwidXNlcl9pZCI6MX0=') Out[3]: b'17ee51ecd37843a86fde38eb57ee77a097f9b9d8:{"_auth_user_id":"1","_auth_user_backend":"django.contrib.auth.backends.ModelBackend","_auth_user_hash":"b7f4334871926f454bae9427b2e873cee0810efe","user_id":1}'
こちらも key:value
という形になっている。なんだか value に auth_user 系のものがついてて、DBの場合挙動が違うかと思ったけど、単純に admin にログインしたままだった。
ログアウトするとこう。
In [6]: base64.b64decode('Njk2ZWY1MDRmMzcwZjA0NGNlZDQzNzc5YTdjMWY3NjIwMmVhMjBkNDp7InVzZXJfaWQiOjF9') Out[6]: b'696ef504f370f044ced43779a7c1f76202ea20d4:{"user_id":1}'
どういう実装になっているか(backend)
基本は base.py にある SessionBase を継承している。クラス名は SessionStore
以下のメソッドを実装する必要がある
- exists
- create
- save
- delete
- load
- clear_expired
file
os.write, shutil, tempfile あたりをつかっている。あんまり馴染みがなかったので tempfile
を使ってみる。
In [1]: import tempfile In [2]: tempfile. tempfile.NamedTemporaryFile tempfile.TMP_MAX tempfile.TemporaryFile tempfile.gettempprefix tempfile.mkstemp tempfile.tempdir tempfile.SpooledTemporaryFile tempfile.TemporaryDirectory tempfile.gettempdir tempfile.mkdtemp tempfile.mktemp tempfile.template In [2]: tempfile.get tempfile.gettempdir tempfile.gettempprefix In [2]: tempfile.gettempdir() Out[2]: '/var/folders/fd/3vccp68n6n50prd2zj2x67mc0000gn/T' In [3]: tempfile.gettempprefix() Out[3]: 'tmp'
ちなみに上のほうで出てきたディレクトリを指定する必要があったという判定は、こういうコードだった。
# Make sure the storage path is valid. if not os.path.isdir(storage_path): raise ImproperlyConfigured( "The session storage path %r doesn't exist. Please set your" " SESSION_FILE_PATH setting to an existing directory in which" " Django can store session data." % storage_path)
db
Session
というモデルを使って CRUD していた。
おわりに
fileの場合サーバーが複数台になるとそれぞれのサーバー上でセッションが保存されることになるので、リクエストしたタイミングなどによってはセッションが取得できない。そのセッションファイルはそれぞれのサーバー上の tempfile で生成される一時的なディレクトリに存在している。DBに保存しているのであれば、masterとなるDBがひとつなので、セッションファイルがわかれることはなかった。ただ、masterが複数台になるようなパターンがあったらだめなのでは。
という、一言で言えばそういう話。