Google App Engine/Pythonでセッションを使う方法

Google App Engine/Pythonのセッションのバックエンドを、DjangoとMemecacheを使って実装する方法です。以前、「Djangoのセッションエンジンを自作する方法」と「GAE/Pでセッションエンジンのバックエンドをmemcacheにするサンプル」という記事を投稿しましたが、それの使い方までは書いていませんでした。ここではサンプルを通して、その作成したものをどう使うのかを載せたいと思います。

自作したセッションを使ったサンプル

それでは、早速セッションのバックエンドをMemecacheにしたサンプルを載せたいと思います。そのサンプルが持つ機能ですが、ただ単に画面の表示回数を計測するというシンプルなものです。また、その回数をリセットするボタンも実装しています。

サンプルで使うファイル一覧

この記事のサンプルは以下のファイルで構成されます。なお、下記ファイルはすべて同一ディレクトリ上に配置します。

  1. app.yaml
  2. main.py
  3. settings.py
  4. memcache_session.py
  5. urls.py
  6. views.py
  7. index.html

app.yamlの作成

Google App Engineのアプリに必要なapp.yamlを作成します。特に複雑なことはしておりません。下記コードの[Application Identifier]は変更する必要があります。

application: [Application Identifier]
version: 1
runtime: python
api_version: 1
handlers:
- url: .*
  script: main.py

↑ サンプルで使うファイル一覧に戻る

main.pyの作成

リクエストがあった際に最初に実行されるmain.pyを作成します。これも特に複雑なことはしておりません。

import os
os.environ['DJANGO_SETTINGS_MODULE'] = 'settings'
from google.appengine.dist import use_library
use_library('django', '1.2')
import django
from django.conf import settings
import django.core.handlers.wsgi
settings._target = None
from google.appengine.ext.webapp import util

def main():
    application = django.core.handlers.wsgi.WSGIHandler()
    util.run_wsgi_app(application)

if __name__ == '__main__':
    main()

↑ サンプルで使うファイル一覧に戻る

settings.pyの作成

Google App Engineの設定ファイルを作成します。ここではセッションのエンジンに、Memcacheを使って自作したやつ指定しています。

#coding: utf-8

import os
SECRET_KEY = '__SECRET_KEY__'
TEMPLATE_LOADERS = (
    'django.template.loaders.filesystem.Loader',
)
MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
)
ROOT_URLCONF = 'urls'
TEMPLATE_DIRS = (
    os.path.dirname(os.path.realpath(__file__)),
)

# セッションのエンジンを作ったものを指定します。
SESSION_ENGINE = 'memcache_session'

SESSION_COOKIE_AGE = 1209600
SESSION_COOKIE_DOMAIN = ''
SESSION_COOKIE_NAME = 'gpsessionid'
SESSION_COOKIE_PATH = '/'
SESSION_COOKIE_SECURE = False
SESSION_EXPIRE_AT_BROWSER_CLOSE = False
SESSION_SAVE_EVERY_REQUEST = False

↑ サンプルで使うファイル一覧に戻る

memcache_session.pyの作成

バックエンドをMemcacheにしたセッションを作成します。といっても基本的な骨組み、例えばランダムなキーを生成する処理などはDjangoのものをそのまま使うので、たいした処理は実装していません。

#!/usr/bin/env python
#coding: UTF-8

import logging

from django.contrib.sessions.backends import base

from google.appengine.api import memcache


class SessionStore(base.SessionBase):

    def __init__(self, session_key=None):
        '''初期処理を実行します。'''
        self._session_namespace = '__SESSION_NAMESPACE__'
        super(SessionStore, self).__init__(session_key)

    def load(self):
        '''セッションをロードします。'''
        # セッションキーを使ってmemcacheから辞書型のオブジェクトを取得します。
        data = memcache.get(self.session_key,
                namespace=self._session_namespace)
        if data:
            return data
        else:
            # まだデータが作成されていない場合、セッションを新規に作成します。
            self.create()
            return {}

    def exists(self, session_key):
        '''セッションキーの重複をさけるために、
        既に登録されているかを判定します。'''
        data = memcache.get(session_key, namespace=self._session_namespace)
        if data:
            return True
        else:
            return False

    def create(self):
        '''新しくセッションを生成します。'''
        while True:
            # 新しいセッションキーを生成します。
            self.session_key = self._get_new_session_key()
            try:
                # セッションを保存します。
                self.save(must_create=True)
            except base.CreateError:
                continue
            self.modified = True
            self._session_cache = {}
            return

    def save(self, must_create=False):
        '''セッションを保存します。
        Args:
            must_create: Trueの場合、新規作成されなかった場合はエラーを投げます。
        Raises:
            base.CreateError: must_createがTrueの場合、新規作成に失敗した場合に
                              発生します。
                              must_createがFalseの場合、新規作成か更新に失敗した
                              場合に発生します。
        '''
        if must_create:
            is_success = memcache.add(
                    self.session_key,
                    self._get_session(no_load=must_create),
                    time=self.get_expiry_age(),
                    namespace=self._session_namespace)
        else:
            is_success = memcache.set(
                    self.session_key,
                    self._get_session(no_load=must_create),
                    time=self.get_expiry_age(),
                    namespace=self._session_namespace)

        if not is_success:
            raise base.CreateError

    def delete(self, session_key=None):
        '''セッションを削除します。'''
        if session_key is None:
            if self._session_key is None:
                return
            session_key = self._session_key
        memcache.delete(session_key,
                namespace=self._session_namespace)

↑ サンプルで使うファイル一覧に戻る

urls.pyの作成

Djangoのルーティングを決めるファイルを作成します。画面表示とカウンタリセットの2つのパターンを用意します。

from django.conf.urls.defaults import *

urlpatterns = patterns('',
    (r'^$', 'views.index'),
    (r'^reset$', 'views.reset'),
)

↑ サンプルで使うファイル一覧に戻る

views.pyの作成

セッションから取得したカウンタを一つ増やし画面を表示させる関数と、カウンタをリセットする関数を実装したビューを作成します。

#coding: UTF-8

from django import shortcuts
from django.core import urlresolvers

def index(request):
    '''カウンタを1つ増やしてから、画面を表示するビューです。'''
    # [counter]keyを使って、sessionから画面表示回数を取得します。
    counter = request.session.get('counter', 0)

    counter += 1
    # sessionを更新します。
    request.session['counter'] = counter
    return shortcuts.render_to_response('index.html', dict(counter=counter))

def reset(request):
    '''カウンタをリセットするビューです。リセット後はindexにリダイレクトします。'''
    # sessionから[counter]keyで設定した値を削除します。

    if 'counter' in request.session:
        del request.session['counter']
    return shortcuts.redirect(urlresolvers.reverse(index))

↑ サンプルで使うファイル一覧に戻る

index.htmlの作成

画面表示用のテンプレートを作成します。カウンタの表示と、「Reload」と「Reset」の2つのボタンを表示するだけのシンプルな画面です。

<html>
  <head>
    <meta charset="utf-8" />
    <title>Google App Engineでセッションを使うサンプル</title>
  </head>
  <body>
    <p>COUNTER: {{ counter }}</p>
    <input type="button" value="Reload" onclick="location.href='/'" />
    <input type="button" value="Reset" onclick="location.href='reset'" />
  </body>
</html>

↑ サンプルで使うファイル一覧に戻る

以上が、バックエンドをMemcacheにしたセッションエンジンを使うサンプルになります。

この記事が役に立った場合、シェアしていただけると励みになります!!