LoginSignup
5

More than 5 years have passed since last update.

Python Pyramidのトラバーサルについて

Posted at

今更ながらPythonのWebフレームワークPyramidを触ってみた。
この記事は、「インストールしてみたwww」的な初歩の初歩は省き、「じゃあ実際に実装するときどうするの」とか、引っかかりそうなところに焦点を当てた備忘録である。

トラバーサル(Traversal)

Pyramidには、おなじみURLディスパッチの他にTraversal(トラバーサル)というURLマッピング方法がある。

トラバーサルについては、本家英語サイトの説明では概念的なことしか記載していないし、広大なネット上を調べてみても、数件の記載はあるがそれぞれどうやら解釈(?)が違うようで実装方法が違う。

しかし、アクセスコントロールを実装するためには、本家サイトの例にもあるようにトラバーサル使わないといけないという仕様らしく、PyramidでWebサイトを作る以上、ここを避けては通れない。

トラバーサルの概念とPythonの辞書

本家にトラバーサルの概念については記載があるため詳しい説明は省くが、簡単に言うと、URLの各要素を入れ子になったPythonの辞書のキー値に見立て、最後に残ったキーをビュー名、最後にヒットした要素をコンテキスト(操作対象)とするURLマッピングの方法である。

たとえば、あるサイトのアカウント'testuser'のユーザ情報なんかを編集するためのURLが"http://example.com/account/testuser/edit" となっていた場合、トラバーサルでは

  • Root['account']['testuser']で得られる要素がリソース
  • editがビュー名

となる。
上のRootとは便宜上記述したものであるが、実際にはトラバーサルの開始要素で、ユーザーからのリクエストがあるたびに生成されるリソースである。誤謬があるが、http://example.com がこのサイトのrootリソースと言っても良い。

適当に要約すると上記のように書いてあるが、すぐ壁にぶち当たる。

トラバーサルを使ったユーザアカウントの実装

上述した内容は、既に本家サイトに(英文だが)記載がある。
本家サイトを鵜呑みにすると、ユーザアカウント関係のリソースの実装としては以下の様になるだろう。

def site_root_factory(request):
    root = dict()
    root['account'] = dict()
    root['account']['testuser'] = request.dbsession.Query(User).filter(name = 'testuser').first()

アタリマエのことだが、こんなもんで実装だと言えるわけがない。
固定的なサイトならまだしも、ユーザアカウントは、’登録’/'削除'ができることが当然で、1つとは限らない、例えば'foouser'が追加された場合はどうするのだろうか?

つまり、トラバーサルで動的なWebサイトを作成しようとするなら、リソースを単なるPythonの辞書とその要素として実装するだけでは実現できないのである。

リソースクラスによる実装方法

リソースとは単なる'辞書dict()'ではなく、__getitem__ メソッドを実装したクラス/オブジェクトのことである。このことが書いてないと初心者には厳しい。
__getitem__ってなに? という人はPython本家のドキュメントを読んでいない証拠なので、今すぐ読んだほうが良い。

上述した例を用いると、キー'account'で得られる要素は__getitem__を実装した以下のような AccountFactoryクラスとして実装することができる。

class AccountResource(object):
    def __init__(self, parent, request, model):
         self.request = request
         self.model = model
         self.parent = parent

    @property
    def __name__(self):
         return self.model.name

    @property
    def __parent__(self):
         return self.parent


class AccountFactory(object):
    def __init__(self, parent, request):
         self.request = request
         self.parent  = parent

    @property
    def __name__(self):
         return 'account'

    @property
    def __parent__(self):
         return self.parent

    def __getitem__(self, key):
         m = self.request.dbsession.Query(User).filter(name = key).first()
         if m: 
             reurn m
         else: 
             raise KeyError(key)


def site_root_factory(request):
    root = dict()
    root['account'] = AccountFactory(root, request)

これで、root['account']['testuser']が呼び出されたときには、AccountFactory__getitem__メソッドがkey='testuser'で呼び出され、Userモデルが最終的なコンテキストとして呼び出されるようになる。

このコンテキスト(Userモデル)を操作するeditビューを定義するには、たとえば以下のようになるだろう。

@view_config(name='edit', context='User', renderer='editview.jinja2')
def edit(context, request)
    # contextはトラバーサルの結果得られたUserモデルである
    # 以下にUserモデルを操作する処理を記載する

何故か、上記例のようにDBアクセスが発生する際のトラバーサルの例がなかったため、ここに記載しておく。

状況

日本語の文献は殆ど無い。お察しである。

あってもお決まりのPyramidのインストールと「起動しました!」の記事。
pipでスパッとインストールできる時代にココらへんのことしかないってのはどうなんだろう。
思うんだけど、本家サイトに書いてあるようなインストールの方法記事にしてどうするんだろう。

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5