過去に、AMP HTML の Blog テーマを作成しました。
Mezzanine AMP 対応のテーマを作成してみました。 | Monotalk
上記テーマと通常のテーマは、Mezzanine の Device Handling という機能を使ってテーマを切り替えており、URLは 通常テーマ も AMPテーマも同様です。
Device Handling — Mezzanine 4.2.3 documentation

現状の実装だと、以下2点が微妙に思いましたので、Django の AMP 関連 のライブラリを参考に、テーマの振り分け処理を変更してみました。

動的な配信  |  Search  |  Google Developers
そして、同一 URL で、通常ページと、AMPページを配信している例を見たことがないのでおそらく一般的なやり方ではないのかと思います。2


参考

実装には、以下 StackOverFlow の記事と、djagno-plugin を参考にしました。
* amp html - Accelerated mobile pages in django? - Stack Overflow


実装、実施したこと

実装は、ほとんどshtalinberg/django-amp-tools: Accelerated mobile pages (AMP) in django拝借して作成しました。
django-amp-tools クエリストリングにより、ページの振り分けを行いますが、クエリストリングではなく、URLで振り分けを実施したかったので、Middleware の処理を修正しています。

実装すること、実施することをまとめると、以下4点です。
* 通常ページ、AMPページのURLでフラグを設定する、middlewareを作成する。
* middleware で設定したフラグを元に、テンプレートを切り替える loaderを作成する。
* settings.py に作成したmiddlewareを定義する。
* urls.py に、AMP のページをマッピングする。

MiddleWare の作成

  • middleware.py
    process_request で、URL に、/amp/含む場合は、フラグを設定しています。
    from amp_start_blog_post import set_amp_detect
    
    class AMPDetectionMiddleware(object):
    
        def __init__(self, get_response=None):
            self.get_response = get_response
            # One-time configuration and initialization.
    
        def __call__(self, request):
            # Code to be executed for each request before
            # the view (and later middleware) are called.
    
            response = self.get_response(request)
    
            # Code to be executed for each request/response after
            # the view is called.
    
            return response
    
        def process_request(self, request):
            if '/amp/' in request.path:
                set_amp_detect(is_amp_detect=True, request=request)
            else:
                set_amp_detect(is_amp_detect=False, request=request)
    

ユーティリティメソッド

__init__.py には、utility メソッドを記載しています。
これは、shtalinberg/django-amp-tools: Accelerated mobile pages (AMP) in django __init__.pyまま使用しています。
threadlocal で、フラグを保持する処理、フラグの値を元に、テンプレートフォルダ名称を返すメソッドが定義されています。

  • __init__py
    # -*- coding: utf-8 -*-
    
    import threading
    from amp_start_blog_post.settings import settings
    
    __version__ = '0.0.1'
    
    _local = threading.local()
    
    
    def set_amp_detect(is_amp_detect=False, request=None):
        request = request or getattr(_local, 'request', None)
        if request:
            request.is_amp_detect = is_amp_detect
        _local.is_amp_detect = is_amp_detect
    
    
    def get_amp_detect(request=None):
        is_amp_detect = False
        request = request or getattr(_local, 'request', None)
    
        # check if is_amp_detect is set on request
        if not is_amp_detect and hasattr(request, 'is_amp_detect'):
            is_amp_detect = request.is_amp_detect
    
        # if set out of a request-response cycle its stored on the thread local
        if not is_amp_detect:
            is_amp_detect = getattr(_local, 'is_amp_detect', False)
        return settings.AMP_TOOLS_TEMPLATE_FOLDER if is_amp_detect else u""
    

Loader の作成

Loader も shtalinberg/django-amp-tools: Accelerated mobile pages (AMP) in django拝借しました。
記載が長いので、Github へのリンクを記載します。

Loader には、Loader と、CachedLoaderあり、CacheedLoader を使用しました。
AMP ページへのアクセスの場合は、AMP のテンプレートを取得する処理を行っています。
Template の格納先フォルダは、AMP_TOOLS_TEMPLATE_FOLDER で、プリフィックスは、AMP_TOOLS_TEMPLATE_PREFIX切り替えることができます。

settings.py の編集

Device Handling の設定を削除、loader の変更を行います。

  • mezzanine Device Handling の設定の削除

    #############################
    # Device Settings
    #############################
    #DEVICE_DEFAULT = ""
    #DEVICE_USER_AGENTS = ( 
    #    ("amp_start_blog_post", ("Android", "BlackBerry", "iPhone" 
    #        )),
    #    )
    

  • loader の変更

          'loaders': [
          ('amp_start_blog_post.loaders.CachedLoader', [
               'amp_start_blog_post.loaders.Loader',
               'django.template.loaders.filesystem.Loader',
               'django.template.loaders.app_directories.Loader',
               ]),
          ],         
    

  • Middleware の追加

      "amp_start_blog_post.middleware.AMPDetectionMiddleware",   
    

urls.py の変更

mezzanine-blog の url を /amp 配下にマッピングします。

  • urls.py
    mezzanine.urls直上に、blog アプリケーションの url をマッピングしました。
    # -----------------------------
    # Mezzanine's Blog app for AMP
    # -----------------------------
    blog_installed = "mezzanine.blog" in settings.INSTALLED_APPS
    if blog_installed:
        BLOG_SLUG = settings.BLOG_SLUG.rstrip("/")
        if BLOG_SLUG:
            BLOG_SLUG += "/"
        blog_patterns = [
            url("^amp/%s" % BLOG_SLUG, include("mezzanine.blog.urls")),
        ]
        urlpatterns += blog_patterns
    
    urlpatterns += [
        url("^", include("mezzanine.urls")),
        # MOUNTING MEZZANINE UNDER A PREFIX
        # ---------------------------------
        # You can also mount all of Mezzanine's urlpatterns under a
        # URL prefix if desired. When doing this, you need to define the
        # ``SITE_PREFIX`` setting, which will contain the prefix. Eg:
        # SITE_PREFIX = "my/site/prefix"
        # For convenience, and to avoid repeating the prefix, use the
        # commented out pattern below (commenting out the one above of course)
        # which will make use of the ``SITE_PREFIX`` setting. Make sure to
        # add the import ``from django.conf import settings`` to the top
        # of this file as well.
        # Note that for any of the various homepage patterns above, you'll
        # need to use the ``SITE_PREFIX`` setting as well.
    
        # ("^%s/" % settings.SITE_PREFIX, include("mezzanine.urls"))
    ]
    

作成した python スクリプト

AMP の テーマファイルの一部として以下、Github リポジトリにチェックインしました。
kemsakurai/mezzanine-theme-amp-start-blog-post: mezzanine theme based by amp start

切り替えとしては、上手くできました。
しばらくたったら、Google Seach Console で様子を見て、問題があれば修正しようかと思います。
以上です。


  1. Vary ヘッダの切り替えは実施していますが、それでも微妙な気がします。 

  2. AMPページは問題なく認識されています。 

コメント