14: JSONレンダラーを使ったAJAX開発(14: AJAX Development With JSON Renderers)

モダンWebアプリケーションはレンダリングされたHTMLより多くの機能があります。動的なページではJSONを用いてサーバーデータのリクエストを行い、JavaScriptを使用してブラウザのUIを更新します。Pyramidはこれを「JSONレンダラー」でサポートしています。

背景(Background)

08:テンプレートを使ったHTML生成(08: HTML Generation With Templating) で見たように、ビューの宣言はレンダラを指定できます。ビューからの出力はレンダラーを介して実行され、レンダラーがレスポンスを生成して返します。最初はChameleonレンダラーを使用してから、Jinja2レンダラーを使用しました。

ただしレンダラーはHTMLを生成するテンプレートだけに制限されません。PyramidはPythonデータを受け取り、それをJSONにシリアル化してコンテンツタイプの設定などの他の機能を実行するJSONレンダラーを提供します。実際には独自のアプリケーションのカスタムロジックを含む独自のレンダラーを作成します(または組み込みレンダラーを拡張する)。

手順(Steps)

  1. 最初に、 view_classes での結果をコピーします:

    $ cd ..; cp -r view_classes json; cd json
    $ $VENV/bin/pip install -e .
    
  2. We add a new route for hello_json in json/tutorial/__init__.py:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    from pyramid.config import Configurator
    
    
    def main(global_config, **settings):
        config = Configurator(settings=settings)
        config.include('pyramid_chameleon')
        config.add_route('home', '/')
        config.add_route('hello', '/howdy')
        config.add_route('hello_json', 'howdy.json')
        config.scan('.views')
        return config.make_wsgi_app()
    
  3. 新しいビューを実装するのではなく、別のデコレータを views.py のビュー hello に「スタック」します:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    from pyramid.view import (
        view_config,
        view_defaults
        )
    
    
    @view_defaults(renderer='home.pt')
    class TutorialViews:
        def __init__(self, request):
            self.request = request
    
        @view_config(route_name='home')
        def home(self):
            return {'name': 'Home View'}
    
        @view_config(route_name='hello')
        @view_config(route_name='hello_json', renderer='json')
        def hello(self):
            return {'name': 'Hello View'}
    
  4. 最後に新しい機能テストが json/tutorial/tests.py に必要です:

     1
     2
     3
     4
     5
     6
     7
     8
     9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    import unittest
    
    from pyramid import testing
    
    
    class TutorialViewTests(unittest.TestCase):
        def setUp(self):
            self.config = testing.setUp()
    
        def tearDown(self):
            testing.tearDown()
    
        def test_home(self):
            from .views import TutorialViews
    
            request = testing.DummyRequest()
            inst = TutorialViews(request)
            response = inst.home()
            self.assertEqual('Home View', response['name'])
    
        def test_hello(self):
            from .views import TutorialViews
    
            request = testing.DummyRequest()
            inst = TutorialViews(request)
            response = inst.hello()
            self.assertEqual('Hello View', response['name'])
    
    
    class TutorialFunctionalTests(unittest.TestCase):
        def setUp(self):
            from tutorial import main
            app = main({})
            from webtest import TestApp
    
            self.testapp = TestApp(app)
    
        def test_home(self):
            res = self.testapp.get('/', status=200)
            self.assertIn(b'<h1>Hi Home View', res.body)
    
        def test_hello(self):
            res = self.testapp.get('/howdy', status=200)
            self.assertIn(b'<h1>Hi Hello View', res.body)
    
        def test_hello_json(self):
            res = self.testapp.get('/howdy.json', status=200)
            self.assertIn(b'{"name": "Hello View"}', res.body)
            self.assertEqual(res.content_type, 'application/json')
    
    
  5. テストを実行します:

    $ $VENV/bin/py.test tutorial/tests.py -q
    .....
    5 passed in 0.47 seconds
    
  6. Pyramidアプリケーションを以下のように実行します:

    $ $VENV/bin/pserve development.ini --reload
    
  7. Open http://localhost:6543/howdy.json in your browser and you will see the resulting JSON response.

分析(Analysis)

以前からビュー関数とメソッドをPythonデータを返すように変更しました。このようなデータ指向のビューレイヤーの変更によってテストロジックの作成が容易になり、テンプレートをビューロジックから切り離せました。

PyramidにはJSONレンダラーとテンプレートレンダラーがあるので、JSONを返すようにする手順は簡単です。JSONを返すようにする場合は同じビューを保持してビューデータのJSONエンコーディングを返却するように設定しました。以下をを行いました:

  • /howdy.json をルート名にマップするルートを追加します。
  • ルート名を既存のビューに関連付ける @view_config を提供します。
  • ビューの設定を「無効」にすると hello_json ルートが表示されて、ルートが一致すると使用されない home.pt テンプレートレンダラーではなくJSONレンダラーが使用されます。

事実、純粋なAJAXスタイルのWebアプリケーションでは、Pyramidのビュー述語を使用して、モダンなAJAX実装によって送信された Accepts: ヘッダーを照合することで既存のルートを再利用できます

PyramidのJSONレンダラは、基本的なPythonのJSONエンコーダを使用しているので長所と短所を受け継いでいます。 たとえばPythonはネイティブJSONのDateTimeオブジェクトをエンコードできません。 PyramidにはJSONレンダラーをカスタムレンダラーで拡張するなどさまざまなソリューションがあります。