「作ればわかる! Google App Engine for Java プログラミング」本をPythonで書いてみる (4) その3 セッション追加

引き続き、「作ればわかる! Google App Engine for Java プログラミング」本にてPythonを修行中。
前回PayPalを追加したので、今回はセッションの追加。


セッションに関しては不勉強なところもあるため、いわゆる徳丸本を参照しながら実装。

■環境

Google App Engineで使えるセッション機構

本体に付属しているセッション機構は無い。
ただ、webapp2のwebapp2_extrasにはSessionsがあるため、そちらを利用する。
webapp2 - Sessions


なお、webapp時代には以下のようなライブラリがあったものの、webapp2時代では利用できない模様。

  比較表で優れているとの記載あり。ただし、webappベースで作られていたようなので、webapp2の今では動かない。
  参考:stackoverflow - gae-sessions equivalent for Python 2.7


  もしgae-sessionsを使うならば、以下を参照のこと。
  stackoverflow - How to use session on Google app engine
  IACC開発ブログ - GAEでセッションを使ってみた by gae-sessions
  Mere Mortals - How to use sessions on Google App Engine with Python and gae-sessions?

  GitHubにコードはあったけれど、動かないとか書かれている。



■webapp2_extrasで、バックエンドにDatastoreを使うときの注意点

何も設定しないままだと、以下のログが出力される。

Traceback (most recent call last):
File "/base/python27_runtime/python27_lib/versions/third_party/webapp2-2.3/webapp2_extras/appengine/sessions_ndb.py", line 18, in
from ndb import model
ImportError: No module named ndb

そのため、以下の3パターンのいずれかで対応する。

1.app.yamlに追記するパターン (今回はこの方法を採用)
libraries:
- name: webapp2
  version: 2.5.1

「version: latest」としてもよいが、望みどおりのバージョンで動かなかったこともあったので、バージョンを具体的に指定した方が良いかと。

2.ndb.pyを追加するパターン

ndb.pyを追加する。SDK1.6.1 pre-releaseでは修正されていた模様だが、1.7.1では再発。

# -*- coding: utf-8 -*-

from google.appengine.ext.ndb import model

参考:devslog - 【GAE/Python2.7】webapp2のsessionを使う方法 - エラーへの対処

3.sys.modulesに追加するパターン

セッション管理するプログラムに以下を追加する。

import sys
from google.appengine.ext import ndb
sys.modules['ndb'] = ndb

参考:stackoverflow - Import Error for User Model



■実装する箇所

自分でも忘れそうなため、メモ。
なお、以下の記事を参考に実装。

import設定

セッションに必要な、以下の3つをimportする

from webapp2_extras import sessions
from webapp2_extras import sessions_memcache
from webapp2_extras import sessions_ndb
セッションを利用するためのベースクラスを作成する

バックエンドで使いたいものをsession内でアンコメントする

class BaseSessionHandler(webapp2.RequestHandler):
    def dispatch(self):
        self.session_store = sessions.get_store(request=self.request)
                                                 
        try:
            webapp2.RequestHandler.dispatch(self)
            
        finally:
            self.session_store.save_sessions(self.response)
            
    @webapp2.cached_property
    def session(self):
        # 以下はmemcache時
        #return self.session_store.get_session(backend='memcache')
        
        # 以下はCookie版
        #return self.session_store.get_session(backend='securecookie')
        
        # Datastore版
        #return self.session_store.get_session(backend='datastore')
セッションを使うクラスの親クラスを変更

前)

class CartPageHandler(webapp2.RequestHandler):

後)

class CartPageHandler(BaseSessionHandler):
出し入れ

入れる)

self.session['test'] = 'message'

出す)

session = self.session.get('test')
webapp2.WSGIApplicationに、configの設定を追加

なお、cookie_argsのsecureは、localhostの場合はhttpsが使えないために、Trueのままではセッションのテストがうまく動作しないことに注意。

config = {}
config['webapp2_extras.sessions'] = {
                                    'secret_key': 'my-secret-key',
                                    'cookie_name' : 'my-session-name',
                                    'cookie_args' : {
                                                     'max_age' : None,       # Noneの場合、クライアント終了時にクッキー削除
                                                     'domain' : None,        # 徳丸本(p218)より
                                                     'path' : '/',
                                                     'secure' : True,       # セキュア属性、https接続なのでTrue
                                                     'httponly': True        # 徳丸本(p219)より、ONにしておく
                                                    },
                                    'backends': {'datastore': 'webapp2_extras.appengine.sessions_ndb.DatastoreSessionFactory',
                                                 'memcache': 'webapp2_extras.appengine.sessions_memcache.MemcacheSessionFactory',
                                                 'securecookie': 'webapp2_extras.sessions.SecureCookieSessionFactory',
                                                }
                                   }
                                       

app = webapp2.WSGIApplication([(URL_START, CartPageHandler),
                               (URL_PAYMENT + '*', PaymentPageHandler),
                               (URL_CANCEL_REDIRECT + '.*', CancelHandler),
                               (URL_CANCEL, CancelPageHandler),
                              ],
                              config=config,
                              debug=debug)
app.yamlへの追記(webob)

現時点では解決しているかもしれないが、以下の記事に従い、IE対応。
The Aspiring Pythoness - Setting Cookie Expiration in WebApp2

libraries:
- name: webob
  version: latest
セッション関連のエラー処理を追加

適宜追加。今回は子クラスで同じようなエラー処理を加えたため、親クラスに追加。



■その他

webapp2ドキュメントにおける、親クラスメソッドの呼出について

super()を使ったほうが良いとのこと。
stackoverflow - AttributeError: 'NoneType' object has no attribute 'route' and webapp2

webapp2のバックエンドについて

以下を見る限り、memcachedとndbのみ実装されている模様。
webapp-improved

NDBとHRDは併用できるのか?

今回の場合、記事データはHRD、セッションはNDBへと保存することになる。併用できるか分からなかったが、今のところは問題なく動作している。(ただし、このままで良いのかはわからない)

SSLに関する記事

以下の記事の考え方が参考になりました。
GitHub - fujieda - SSL is not about encryption


ソースコード

GitHubのchap5に追記しています。