pjaxによるアクセスかどうかを判別するdocorator、Djangoさんの話です。

 先日のpjax話(例のあれ(仮題)- pjaxを導入してみた。)の続きです。

 pjaxではページの一部を変更しますから、やり取りするページのデータも一部でいいです。pjaxでないアクセスの場合は全てのページのデータをやり取りする必要があります。問題はそれらが同一URIでリクエストされる事で、サーバ側で判断してそれぞれに合った形でデータを供給しなければなりません。実際にはpjaxである事が確認できたら一部を、そうでない場合はとりあえず全てのデータを送信するようになると思います。

 前回触れたようにjquery-pjaxではX-PJAXというヘッダーを付加してアクセスしてくれますので、それを利用して判定します。ビューで受け取ったRequestオブジェクトのMETA辞書の中にHTTP_X_PJAX(X-BenderみたいなヘッダーはHTTP_X_BENDERという風に大文字アンダースコア化されて格納されます)があるかどうかでOKですね。

def spam_view(request):
    if 'HTTP_X_PJAX' in request.META:
        pjaxの場合の処理
    else:
        pjaxではない場合の処理
    return spam_response

 とかが基本的な形になりますでしょうか。

 基本がわかったところで、Ajaxのときと同じようにdecoratorにしてしまえばいいかなぁと。これをdecoratorにするのが果たして正解なのかどうかはイマイチ判然としませんけども、私的に便利そうだし書きたかったし。自前アプリで自前アプリ用に実装するので自前仕様上等でありますから、そういうのを幾つか適当に仕込みましたよ。

pjaxかどうか判定し、そうでなければ他のView関数を呼び出して返す。
その場合の呼び出される関数は、元の関数名から_pjaxを削除した名前で同一module内に実装してある。

 後付けで、かつ自前だからこそできる規約的仕様。規約より明記とか言いながら規約。いい加減過ぎる。

def pjax_access(func):
    def _pjax_access(*args, **kwargs):
        if not 'HTTP_X_PJAX' in args[0].META:
            module = __import__(func.__module__, globals(), locals(), [func.__name__.replace('_pjax', '')])
            function = getattr(module, (func.__name__).replace('_pjax', ''))
            return function(*args, **kwargs)
        return func(*args, **kwargs)
    return _pjax_access

 あれこれ試しながら実装したらこうなりました。途中、どうして自分がいるmoduleをすんなりimportできないんだとかなんとか憤ったりしてました。

 せめてViewの名前だけでも渡せるようにしたい場合は、もう一つ関数を入れ子にして、

def pjax_access(view_name):
    def _pjax_access(func):
        def __pjax_access(*args, **kwargs):
            if not 'HTTP_X_PJAX' in args[0].META:
                module = __import__(func.__module__, globals(), locals(), [view_name])
                function = getattr(module, view_name)
                return function(*args, **kwargs)
            return func(*args, **kwargs)
        return __pjax_access
    return _pjax_access

 とでもするといいと思いますよ。もちろんよくわからないままに書きました、そらそうです。

 久しぶりにPythonやったら行末に;付けようとするわ、if (spam !== ham) {とか書きそうになるわで相変わらず頭悪いなって思った。賢くなりたいです。もう遅いですか、そうですね。