セッションの使い方

revision-up-to:17812 (1.4)

Django は匿名セッション (anonymous session) を完全にサポートしています。 セッションフレームワークを使うと、任意のデータをサイト訪問者単位 (per-site-visitor) で保存したり取得したりできます。セッションフレームワーク はサーバ側にデータを保存し、クッキーの送受信操作を抽象化します。クッキーに はセッション ID だけが保存され、データ自体は送受信されません。 ( クッキーベースのバックエンド を使うことなしには)

セッションを有効にする

セッション機能は ミドルウェア として実装されていま す。

セッションを有効にするには、以下の作業が必要です:

  • MIDDLEWARE_CLASSES を編集して、 'django.contrib.sessions.middleware.SessionMiddleware' を入れます。 django-admiin.py startproject の作成するデフォルトの settings.py では SessionMiddleware が有効になっています。

セッション機能を必要としないのなら、 MIDDLEWARE_CLASSES から SessionMiddleware の行を削り、 INSTALLED_APPS からも 'django.contrib.sessions' を削って下さい。セッションを無効にすると、ほんのわずかだけオーバヘッド を軽減できます。

セッションエンジンの設定

デフォルトでは、 Django はセッションをデータベースに (django.contrib.sessions.models.Session モデルを使って) 保存します。こ の仕様は便利ではありますが、場合によっては、データベース以外の場所、ファイ ルシステムやキャッシュ上にセッションデータを保存する方が高速です。

データベースバックエンドセッションを使う

もし、データベースバックエンドセッション(database-backend session)を使いたけれ ば、 INSTALLED_APPS セッティングに 'django.contrib.sessions' を 加える必要があります。

一度、 Django の manage.py syncdb を走らせて、データベースを構成している と、テーブルが一つセッションデータを保存するために使われています。

キャッシュベースのセッション

パフォーマンスを求めるなら、キャッシュベースのセッションバックエンドを使う のがよいでしょう。

Django のキャッシュシステムにセッションデータを保存するには、まずキャッシュ を設定しておく必要があります。詳しくは キャッシュのドキュメント を参照してください。

Warning

Memcached をキャッシュバックエンドとして使っているなら、キャッシュベー スのセッションを使うべきです。ローカルメモリ型のキャッシュバックエンド は十分な時間データを保持できないので、よい選択肢とはいえません。また、 ファイルやデータベースによるキャッシュバックエンドを使っている場合、何 もかもをキャッシュに放り込むより、直接ファイルやデータベースからデータ を出力する方がはるかに高速です。

一度キャッシュが設定されていれば、キャッシュにデータを保存する二つの方法が 選べます。

  • SESSION_ENGINE"django.contrib.sessions.backends.cache" に、設定すると、シンプルにセッションの保存がキャッシングできます。セッション データは、直接キャッシュの中に保存されます。しかし、セッションデータは 永続的ではありません。キャッシュされたデータはキャッシュが一杯になるか、 キャッシュサーバーが再起動された時に消去されてしまいます。
  • キャッシュのデータを永続的にするために、 SESSION_ENGINE"django.contrib.sessions.backends.cached_db" に設定してください。 これは、ライトスルーキャッシュを用います(write-through cache)。 全ての書き込みがキャッシュへといき、またデータベースにも書き込まれます。 セッションは、キャッシュの中にデータが準備されていなければ、データベース しか読みません。

両方のセッションの保存はどちらも十分に高速ですが、シンプルキャッシュは 永続性を放棄した結果、より早くなっています。ほとんどのケースで、 cached_db バックエンドも必要な程度の早さを発揮しますが、少しでも早さを求めるなら、 そしてセッションデータが時とともに消去されていくのをよしとするなら、 cache バックエンドが適しているケースでしょう。

ファイルベースのセッション

ファイルベースのセッションを使うには、 SESSION_ENGINE 設定を "django.contrib.sessions.backends.file" にします。

また、必要に応じて SESSION_FILE_PATH も設定してください (デフォルト値は tempfile.gettempdir() の戻り値で、たいていは /tmp です)。 Web サーバが SESSION_FILE_PATH の場所にファイルの読書き権限を持っているか 確かめてください。

クッキーベースバックエンドセッションを使う

Django 1.4 で新たに登場しました: リリースノートを参照してください

クッキーベースセッションを使うには、 SESSION_ENGINE"django.contrib.sessions.backends.signed_cookies" に設定します。 セッションデータは、 cryptographic signingSECRET_KEY 設定へと Django ツールを使って保存されます。

Note

SESSION_COOKIE_HTTPONLY 設定を True にすると、 JavaScript から、保存されたデータを改ざんすることを妨げることが できるのでお勧めしています。

Warning

セッションデータは認証されていますが、暗号化されていません

クッキーバックエンドを使うと、セッションデータはクライアントから 読むことが出来ます。

MAC ( Message Authentication Code )は、クライアントからのデータの変更から データを守るのに使われます、つまりセッションデータは改ざんされる時に無効 化されるということです。同じような無効化は、クライアントが保存している クッキー(すなわち、ユーザのブラウザが保存しているもの)が、全て保存されない 場合と、データが欠落してしまった場合に起こります。 Django はデータを 圧縮していますが、それは common limit of 4096 bytes を超過しています。

最新である保証がない

MAC がデータの信頼性と(これはあなたのサイトが生成します) データの一貫性(全てがそこにあり、全てが正しいかどうか)を保証する一方、 MAC はデータの最新性を保証しません。すなわち、クライアントに送信した 最後のものが、返ってきているということです。これは、セッションデータが いくつか用いていることを意味します。クッキーバックエンドは 反射攻撃 に耐えて開くだろうということです。 SESSION_COOKIE_AGE よりもクッキーが古い場合、クッキーは ‘stale’ としてのみ検出されます。

パフォーマンス

最後に、大容量のクッキーは、 サイトのスピード に影響を与えます。

ビュー中でセッションを扱う

SessionMiddleware を有効にすると、各々の HttpRequest オブジェクト(Django ビュー関数の最初の引数) は辞書ライクオブジェクトの属性 session を持つようになります。 この属性は読み書き可能です。

ビューのどの部分でも request.session で読み書き可能です 複数回の編集・変更が可能です。

class backends.base.SessionBase

これは、全てのセッションオブジェクトのベースクラス(基底クラス)です。 通常のディクショナリのメソッドをサポートしています。

__getitem__(key)

例: fav_color = request.session['fav_color']

__setitem__(key, value)

例: request.session['fav_color'] = 'blue'

__delitem__(key)

例: del request.session['fav_color'] セッションデータ中に key に対応する値がない場合には KeyError を送出します。

__contains__(key)

例: 'fav_color' in request.session

get(key, default=None)

例: fav_color = request.session.get('fav_color', 'red')

pop(key)

例: fav_color = request.session.pop('fav_color')

keys()
setdefault()
clear()

これは、三つのメソッドを持っています:

flush()

現在のセッションデータをデータベースから削除し、後でクッキーに入れて ユーザに送り返すために新たなセッションキーを生成します。ユーザの使っ ているブラウザに、以前のセッションデータにアクセスさせたくない場合に 使います (例えば django.contrib.auth.logout() で呼び出されま す)。

テストクッキーを設定して、ユーザのブラウザがクッキーをサポートしてい るかどうかを調べられるようにします。クッキーの動作仕様上、次にブラウ ザがリクエストを送信してくるまでテストは行えません。詳しくは後述の 「 テストクッキーを設定する 」を参照してください。

ユーザのブラウザがテストクッキーを受け入れたかどうかに応じて True または False を返します。クッキーの動作仕様上、あらかじめ別のペー ジリクエストで set_test_cookie() を呼び出しておかねばなりません。 後述の「 テストクッキーを設定する 」を参照してください。

テストクッキーを削除します。後始末に使って下さい。

set_expriry(value)

セッションの有効期限をセットします。渡せる値には、以下のバリエーションが あります:

  • value に整数を渡すと、セッションがアクティブでないまま value 秒間経った時点で有効期限が切れます。例えば、 request.session.set_expiry(300) とすると、セッションの有 効期限は 5 分で切れます。
  • valuedatetimetimedelta オブジェクトを渡す と、セッションの有効期限は指定日・時刻に切れます。
  • value0 を渡すと、ユーザセッションクッキーの有効期 限は、ブラウザを閉じた時点で切れます。
  • valueNone を渡すと、セッションの有効期限はグロー バルに設定されているセッションポリシーに戻されます。

セッションの読み込みはアクションの目的が満たされているかは考慮しません。 セッションの満了は、セッションが最後に 変更された かをもとに計算されて います。

get_expiry_age()

セッションの有効期限が切れるまでの秒数を返します。セッションの有効期 限をカスタマイズしていない場合 (またはブラウザを閉じるまでを有効期限 としている場合) は、 settings.SESSION_COOKIE_AGE と等しい値です。

get_expiry_date()

セッションの有効期限が切れるまでの日数を返します。セッションの有効期 限をカスタマイズしていない場合 (またはブラウザを閉じるまでを有効期限 としている場合) は、 SESSION_COOKIE_AGE 秒に相当する日数 です。

get_expire_at_browser_close()

ユーザのセッションクッキーの有効期限がブラウザを閉じるまでに設定され ていれば True を、そうでなければ False を返します。

セッションオブジェクト使用上のガイドライン

  • request.session のキーには通常の Python 文字列を使って下さい。と はいえ、これは厳格な掟 (hard-and-fast rule) ではなく単なる規約です。
  • アンダースコアで始まるセッション辞書のキーは Django の内部使用のため に予約されています。
  • request.session を新たなオブジェクトでオーバライドしたり、属性を いじってはなりません。Python 辞書型のように扱って下さい。

使用例

以下の簡単なビューの例では、ユーザがコメントをポストした後に has_commented という変数を True に設定しています。これにより、一人 のユーザに一つのコメントを何度もポストさせないようにします:

def post_comment(request, new_comment):
    if request.session.get('has_commented', False):
        return HttpResponse("You've already commented.")
    c = comments.Comment(comment=new_comment)
    c.save()
    request.session['has_commented'] = True
    return HttpResponse('Thanks for your comment!')

以下のビューでは、「メンバ」をサイトにログインさせます:

def login(request):
    m = Member.objects.get(username__exact=request.POST['username'])
    if m.password == request.POST['password']:
        request.session['member_id'] = m.id
        return HttpResponse("You're logged in.")
    else:
        return HttpResponse("Your username and password didn't match.")

そして下の例では、上の例で login() したメンバをログアウトさせます:

def logout(request):
    try:
        del request.session['member_id']
    except KeyError:
        pass
    return HttpResponse("You're logged out.")

実際には、標準の django.contrib.auth.logout() は、うっかりデータが漏洩 してしまうのを防ぐために、 request.sessionflush() を呼び出しています。 上の例はセッションオブジェクトの仕組みを説明するためのもので、完全な logout() の実装ではありません。

テストクッキーを設定する

利便性のために、 Django ではユーザのブラウザがクッキーを受け入れるかどうか を調べるための簡単な方法を提供しています。ビュー内で request.sessionset_test_cookie() を呼び出しておき、 それ以後のビュー、すなわち別のビュー呼び出しで test_cookie_worked()`() を呼び出すようにしてください。

set_test_cookie()test_cookie_worked() が別々のビュー呼び出しに 分離されるのは不恰好ですが、これはクッキーの動作上仕方のないことです。ある ブラウザに対して一度クッキーを設定しても、そのブラウザが次にリクエストを送 信するまではクッキーを受け入れたかどうかを確かめる術はないのです。

テストが終わったら、 delete_test_cookie() を呼び出して後始末をしておくのがよいでしょう。

クッキーの動作テストが終わった時点で、この関数を呼び出して下さい。

典型的な使用例を以下に示します:

def login(request):
    if request.method == 'POST':
        if request.session.test_cookie_worked():
            request.session.delete_test_cookie()
            return HttpResponse("You're logged in.")
        else:
            return HttpResponse("Please enable cookies and try again.")
    request.session.set_test_cookie()
    return render_to_response('foo/login_form.html')

ビューの外でセッションを使う

API を使うと、ビューの外からセッションデータを操作できます:

>>> from django.contrib.sessions.backends.db import SessionStore
>>> import datetime
>>> s = SessionStore(session_key='2b1189a188b44ad18c35e113ac6ceead')
>>> s['last_login'] = datetime.datetime(2005, 8, 20, 13, 35, 10)
>>> s['last_login']
datetime.datetime(2005, 8, 20, 13, 35, 0)
>>> s.save()

もし、 session_key が与えられていなければ、自動で生成されます。:

>>> from django.contrib.sessions.backends.db import SessionStore
>>> s = SessionStore()
>>> s.save()
>>> s.session_key
'2b1189a188b44ad18c35e113ac6ceead'

django.contrib.sessions.backends.db バックエンドを使っている場合、各セッ ションは Django のモデルインスタンスで表現されています。 Session モデル は django/contrib/session/models.py で定義されています。 Session は 通常のモデルなので、通常の Django データベース API でアクセスできます:

>>> from django.contrib.sessions.models import Session
>>> s = Session.objects.get(pk='2b1189a188b44ad18c35e113ac6ceead')
>>> s.expire_date
datetime.datetime(2005, 8, 20, 13, 35, 12)

セッション情報の辞書を取得するには get_decoded() を呼び出す必要があるの で注意して下さい。というのも、セッション情報はエンコードされた形式で保存さ れているからです。

>>> s.session_data
'KGRwMQpTJ19hdXRoX3VzZXJfaWQnCnAyCkkxCnMuMTExY2ZjODI2Yj...'
>>> s.get_decoded()
{'user_id': 42}

セッションはいつ保存されるのか

デフォルトでは、 Django はセッション情報が変更された場合、すなわちセッショ ン情報の入った辞書に値を代入したり、値を削除した場合にのみ、セッションデー タベースを保存します:

# セッションデータは変更されたものとみなされます
request.session['foo'] = 'bar'

# セッションデータは変更されたものとみなされます
del request.session['foo']

# セッションデータは変更されたものとみなされます
request.session['foo'] = {}

# 落とし穴: request.session ではなく request.session['foo'] の変更なの
# で、セッションデータは変更されたものとみなされません。
request.session['foo']['bar'] = 'baz'

上の例の最後のケースでは、セッションオブジェクトに内容が変更されたことを明 示的に教えねばなりません。変更の通知は modified 属性で行います:

request.session.modified = True

この振舞いを変更したければ、 SESSION_SAVE_EVERY_REQUEST 設定を True に設定してください。 SESSION_SAVE_EVERY_REQUESTTrue にすると、 Django はリクエスト一つ一つに対してセッションを保存します。

セッションクッキーはセッションが作成されたり変更されたりした場合にのみ送信 されることに注意してください。 SESSION_SAVE_EVERY_REQUESTTrue にすると、リクエストごとに必ずセッションクッキーを送信するようになります。

同様に、セッションクッキーの expires 部分もセッションクッキーの送信ごと に更新されます。

ブラウザアクセス単位のセッションと永続的セッション

SESSION_EXPIRE_AT_BROWSER_CLOSE 設定を使うと、セッションフレームワーク に、ブラウザアクセス単位のセッションと永続的セッションのどちらを使わせるか を指定できます。

デフォルトでは、 SESSION_EXPIRE_AT_BROWSER_CLOSEFalse に設定さ れています。これはセッションクッキーが SESSION_COOKIE_AGE の間だけブラ ウザに保存されることを示します。ユーザがブラウザを起動するたびにログインし なくてもすむようにしたければ、この設定を使ってください。

SESSION_EXPIRE_AT_BROWSER_CLOSETrue にすると、 Django はブラウ ザアクセス単位のクッキー、すなわちユーザがブラウザを閉じると有効期限が切れ るようなクッキーを使うようになります。ブラウザを起動するたびにユーザにログ イン操作を行わせたい場合、この設定を使ってください。

この設定は、グローバルに有効なデフォルト値です。 request.session のメソッドである「 ビューの外でセッションを使う 」の節で解説した、 set_expiry()`() を使えば、セッション単位で有効期限をオーバ ライドできます。

セッションテーブルの消去

セッションデータは django_session データベーステーブル上に蓄積されます が、 Django はセッションテーブルを自動的に清掃 しません。 つまり、期限切 れの (expired) セッションデータを正しい判断基準の下に削除するのは、アプリケー ション開発者であるあなた自身の仕事なのです。

この問題を理解するには、ユーザがセッションを使ったときに何が起きているかを 考える必要があります。ユーザがログインすると、 Django は django_session データベーステーブルにレコードを 1 行追加します。セッションデータが変更され る度に、このレコード行は更新されてゆきます。ユーザが手動でログアウトすれば、 レコード行は削除されますが、ユーザがログアウト操作を しなかった場合 には、 レコード行は削除されません。

Django は、クリーンアップ用のアクション、 django-admin.py cleanup を提 供しています。このスクリプトはセッションテーブルの全てのエントリの中から、 expire_date の値が過去を指しているものを除去します。もちろん、お使いの アプリケーションが要求する仕様が異なる場合には、別のスクリプトを用意する必 要があるでしょう。

設定

Django 設定ファイル には、セッションの振舞いを操作す るための設定がいくつかあります:

SESSION_ENGINE

デフォルト値: django.contrib.sessions.backends.db

Django がセッションデータを保存する方法を指定します。利用できる値は以下の通 りです:

  • 'django.contrib.sessions.backends.db'
  • 'django.contrib.sessions.backends.file'
  • 'django.contrib.sessions.backends.cache'
  • 'django.contrib.sessions.backends.cached_db'
  • 'django.contrib.sessions.backends.signed_cookies'

詳しくは セッションエンジンの設定 を参照してください。

SESSION_FILE_PATH

デフォルト値: /tmp/

ファイルベースのセッションストレージを使っている場合、この値でセッションデー タの保存場所を指定します。

SESSION_EXPIRE_AT_BROWSER_CLOSE

Default: False

ブラウザを閉じたときにセッションを期限切れにするかどうかを決めます。

SESSION_SAVE_EVERY_REQUEST

デフォルト値: False

リクエストごとにセッションデータを保存するかどうかを決めます。この値が False (デフォルト) の場合、セッションデータの保存は内容が変更された場合、 すなわちセッションデータ辞書に値を設定したり、値を削除したりした場合だけに なります。

技術的な詳細

  • セッション辞書には pickle 化可能な全てのオブジェクトを使えます。詳し くは pickle モジュール を参照してください。
  • セッション情報は django_session という名前のデータベーステーブルに 保存されます。
  • Django は必要なときにしかクッキーを送信しません。従って、セッション情 報を設定しない限り、セッションクッキーの送信を行いません。

URL と Session ID

Django のセッションフレームワークは完全なクッキーベースであり、クッキー以外 の情報を扱いません。従って、 PHP のように URL にセッション ID を入れる方法 を最後の手段に残したりはしていません。これは設計上の意図的な決定です。とい うのも、URL にセッション ID を含めると、みっともない URL になるだけでなく、 “Referer” ヘッダを使ってセッション ID を盗まれるという脆弱性を招くからです。