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が複数台になるようなパターンがあったらだめなのでは。
という、一言で言えばそういう話。
Redmineのチケット一覧結果をシェルスクリプトで整形する
結果
- Redmineの任意のチケット一覧をCSV形式でダウンロードする
iconv -f SJIS -t UTF-8 /path/to/issues.csv | sort -r -k 3 -t , | awk -F ',' '{printf "[%s] #%s %s\n", $3, $1, $7}'
こういうアウトプットになる。
[トラッカー] ## 題名 [機能] #XXXXX XXする [機能] #XXXXX YYする [タスク] #XXXXX 【XX】デプロイする [タスク] #XXXXX XXを設定する [バグ] #XXXXX XXの修正 [バグ] #XXXXX XX YY ZZ エラー対策
Macの場合最後に | pbcopy
などいれるとクリップボードに入って便利。あと [トラッカー]
あたりは除外したほうがよいかも。
背景
Redmineを普段使っている。通常はRedmineのオンラインのチケット上で管理、やりとりをしているけれど、ローカルのテキストファイルで扱いたい場合がある。そのときに簡単な整形をしたい。
前は Python で CSV を読み込んでテキストを出力するする小さなスクリプトを書いていたのだけど、毎回起動してメンテし拡張するのがだるくて結局使わなくなってしまった*1。こういう操作はシェルスクリプトのほうが楽なことが多い。あと wc
, grep
などの操作はシェルのコマンドとパイプの強力さをそのまま使ったほうが便利という印象。書いてて楽しいし。
CSV を処理するときに文字コードを変換する iconv
を使ってみて便利だったというのがこれを書いたきっかけ。ではいままでどうしていたかというと、CSVの話とかぶるけれど、文字コードの変換を伴う作業はだいたい Python 上で処理していた。なんとなくやらないでいたけど、特にやらない理由もなかった。
その他
毎回 awk
の文法を使っては忘れるので、 perl
か ruby
を試そうとしてはだらだらしてしまってやらないままになってる……。
mecurial_keyring が有効な hg コマンドを常に使う話
結論
- python2 の virtualenv を作って、そこに
mercurial
keyring
mercurial_keyring
をインストールする - bashrc, zshrc などに
alias hg='/path/to/virtualenv/bin/hg'
と設定する
mercurial_keyring について
普段 mercurial
を使っている。そのときのパスワード管理には mercurial_keyring
という keyring
を使うようにしてくれるライブラリがある。 keyring
は名前の通りパスワード管理ライブラリ。 keyring
の backend はいくつか種類があるようだけど、OS X の keychain も対応している。 git
の場合は git credential-osxkeychain
というコマンドで keychain で管理できるけれど、それと似たようなものという認識*1。
mercurial_keyring
の使用方法はドキュメントにあるとおり .hgrc
に以下のように設定する。
個人的には環境個別の設定は .hgrc.local
というような別ファイルにして include するようにしている*2
[extensions] mercurial_keyring = [auth] myremote.prefix = my.server.com/hgrepo myremote.username = myname
経緯
python のバージョン、 mercurial
のバージョン、 mercurial_keyring
のバージョンが合わないと *** failed to import extension mercurial_keyring: No module named mercurial_keyring
というエラーがでる。
stackoverflow.com
職場のマシンと私物のマシン、あるいはVMなどを行き来しているうちに最近は使わなくなっていた。extensions に path を指定するやりかたや mercurial
と mercurial_keyring
のバージョンを下げて対応などしていたこともあったけれど、面倒になると多少雑な感じになってきて、直接 .hgrc に書くか毎回パスワードをいれて対応していた。特に最近は python3 を使っていることが増えてきたので、 mercurial
は python3では動かないという問題もあった。あと mercurial
は python 開発でなくても使うことがあるので、 virtualenv にいれたりシステムにいれたりして混在していた。
そんな感じで放置していたのだけど、今日ふと mercurial_keyring
の話が出たので、そういえば、と思いついて設定してみたらあっさりできてしまった。
よく考えたら、virtualenv に python2 で mercurial
と mercurial_keyring
をいれればよいだけだった。常にそれを参照したいなら単純にパスを指定すればよいという話だった。
わりと便利という感じ。
参考
*1:順番的には git を使う機会があった時に mercurial にもあるだろう、ということで探したのがこのライブラリを知るきっかけだった
*2: ちょっと内容が古いけどこういう設定 https://github.com/altnight/dotfiles/blob/master/.hgrc