Spec-Zone.ru › Wagtail 3

Как создать сайт с поддержкой AMP

Данный документ описывает метод создания версии сайта Wagtail с поддержкой AMP и размещения её отдельно от основного сайта на префиксе URL. Он также описывает, как заставить Wagtail отображать изображения с тэгом <amp-img> при посещении пользователем страницы AMP-версии сайта.

Обзор

В следующем разделе мы добавим новую запись URL, которая указывает на внутренний просмотр Wagtail serve(), что приведет к повторному отображению всего сайта по префиксу /amp.

Затем мы добавим некоторые утилиты, которые позволят отслеживать, находится ли текущий запрос в версии сайта с префиксом /amp, без необходимости объекта запроса.

После этого мы добавим обработчик контекста шаблона, чтобы позволить проверять внутри шаблонов, какая версия сайта отображается.

Затем, наконец, мы изменим поведение тэга {% image %} для отображения тэгов <amp-img> при отображении AMP-версии сайта.

Создание второго дерева страниц

Мы можем отобразить весь сайт по другому префиксу, дублировав URL Wagtail в файле проекта urls.py и присвоив ему префикс. Это необходимо сделать до стандартного URL Wagtail, иначе система будет пытаться найти /amp как страницу:

# <project>/urls.py

urlpatterns += [
    # Add this line just before the default ``include(wagtail_urls)`` line
    path('amp/', include(wagtail_urls)),

    path('', include(wagtail_urls)),
]

Если теперь открыть http://localhost:8000/amp/ в вашем браузере, вы должны увидеть главную страницу.

Обеспечение отображения страниц в режиме AMP

Теперь все страницы будут отображаться по префиксу /amp, но сейчас нет никакой разницы между AMP-версией и обычной версией.

Для внесения изменений нам нужно добавить способ определения URL, используемого для отображения страницы. Для этого нам нужно обернуть просмотр Wagtail serve() и установить локальную переменную для потока, чтобы указать всем последующим частям кода, что активен режим AMP.

Примечание

Почему локальная переменная для потока?

(можете пропустить эту часть, если вас это не интересует)

Изменение объекта request было бы наиболее распространенным способом сделать это. Однако рендеринг тега изображения выполняется в части Wagtail, которая не имеет доступа к запросу.

Локальные переменные для потока — это глобальные переменные, которые могут иметь разные значения для каждого потока. Так как каждый поток обрабатывает только один запрос за раз, мы можем использовать его как способ передачи данных, специфичных для этого запроса, без необходимости передачи объекта запроса повсюду.

Django использует локальные переменные для потока для отслеживания текущего активного языка для запроса.

Python реализует локальные переменные для потока через класс threading.local, но начиная с Django 3.x, несколько запросов могут обрабатываться в одном потоке, и поэтому локальные переменные для потока больше не будут уникальны для одного запроса. Поэтому Django предоставляет asgiref.Local в качестве прямой замены.

Теперь давайте создадим эту локальную переменную для потока и некоторые утилитарные функции для взаимодействия с ней. Сохраните этот модуль как amp_utils.py в приложении вашего проекта:

# <app>/amp_utils.py

from contextlib import contextmanager
from asgiref.local import Local

_amp_mode_active = Local()

@contextmanager
def activate_amp_mode():
    """
    A context manager used to activate AMP mode
    """
    _amp_mode_active.value = True
    try:
        yield
    finally:
        del _amp_mode_active.value

def amp_mode_active():
    """
    Returns True if AMP mode is currently active
    """
    return hasattr(_amp_mode_active, 'value')

Этот модуль определяет две функции:

  • activate_amp_mode — это контекстный менеджер, который можно вызывать с помощью синтаксиса Python with. В теле инструкции with режим AMP будет активен.
  • amp_mode_active — это функция, которая возвращает True при активном режиме AMP.

Далее, нам нужно определить представление, которое оборачивает встроенное представление Wagtail serve и вызывает контекстный менеджер activate_amp_mode.

# <app>/amp_views.py

from django.template.response import SimpleTemplateResponse
from wagtail.views import serve as wagtail_serve

from .amp_utils import activate_amp_mode

def serve(request, path):
    with activate_amp_mode():
        response = wagtail_serve(request, path)

        # Render template responses now while AMP mode is still active
        if isinstance(response, SimpleTemplateResponse):
            response.render()

        return response

Затем нам нужно создать файл amp_urls.py в том же приложении:

# <app>/amp_urls.py

from django.urls import re_path
from wagtail.urls import serve_pattern

from . import amp_views

urlpatterns = [
    re_path(serve_pattern, amp_views.serve, name='wagtail_amp_serve')
]

Наконец, нам нужно обновить основной файл проекта urls.py для использования этого нового файла URL для префикса /amp:

# <project>/urls.py

from myapp import amp_urls as wagtail_amp_urls

urlpatterns += [
    # Change this line to point at your amp_urls instead of Wagtail's urls
    path('amp/', include(wagtail_amp_urls)),

    re_path(r'', include(wagtail_urls)),
]

После этого никакой заметной разницы в AMP-версии сайта не должно быть.

Напишите обработчик контекста шаблона, чтобы можно было проверить состояние AMP в шаблонах

Это необязательно, но стоит сделать, чтобы убедиться, что все работает.

Добавьте файл amp_context_processors.py в ваше приложение, содержащий следующее:

# <app>/amp_context_processors.py

from .amp_utils import amp_mode_active

def amp(request):
    return {
        'amp_mode_active': amp_mode_active(),
    }

Теперь добавьте путь к этому обработчику контекста в ключ ['OPTIONS']['context_processors'] настроек TEMPLATES.

# Either <project>/settings.py or <project>/settings/base.py

TEMPLATES = [
    {
        ...

        'OPTIONS': {
            'context_processors': [
                ...
                # Add this after other context processors
                'myapp.amp_context_processors.amp',
            ],
        },
    },
]

Теперь вы можете использовать переменную amp_mode_active в шаблонах. Например:

{% if amp_mode_active %}
    AMP MODE IS ACTIVE!
{% endif %}

Использование другого шаблона страницы, когда активен режим AMP

Вероятно, вы не захотите использовать одни и те же шаблоны на сайте AMP, что и на обычном сайте. Давайте добавим немного логики, чтобы Wagtail использовал отдельный шаблон всякий раз, когда страница отображается с включенным AMP.

Мы можем использовать миксин, который позволяет повторно использовать логику для разных типов страниц. Добавьте следующее в конец файла amp_utils.py, который вы создали ранее:

# <app>/amp_utils.py

import os.path

...

class PageAMPTemplateMixin:

    @property
    def amp_template(self):
        # Get the default template name and insert `_amp` before the extension
        name, ext = os.path.splitext(self.template)
        return name + '_amp' + ext

    def get_template(self, request):
        if amp_mode_active():
            return self.amp_template

        return super().get_template(request)

Теперь добавьте этот миксин к любой модели страницы, например:

# <app>/models.py

from .amp_utils import PageAMPTemplateMixin

class MyPageModel(PageAMPTemplateMixin, Page):
    ...

Когда активен режим AMP, вместо стандартного будет использоваться шаблон по адресу app_label/mypagemodel_amp.html.

Если у вас другой соглашение об именовании, вы можете переопределить атрибут amp_template в модели. Например:

# <app>/models.py

from .amp_utils import PageAMPTemplateMixin

class MyPageModel(PageAMPTemplateMixin, Page):
    amp_template = 'my_custom_amp_template.html'

Переопределение тега {% image %} для вывода тегов <amp-img>

Наконец, давайте изменим тег изображения Wagtail {% image %}, чтобы он выводил тэги <amp-img> при отображении страниц с включенным AMP. Мы внесем изменения в модель Rendition напрямую, чтобы это касалось как изображений, сгенерированных с помощью тега {% image %}, так и изображений в полях rich text.

С Настраиваемой моделью изображения это проще, так как вы можете переопределить метод img_tag в вашей настраиваемой модели Rendition для возврата другого тега.

Например:

from django.forms.utils import flatatt
from django.utils.safestring import mark_safe

from wagtail.images.models import AbstractRendition

...

class CustomRendition(AbstractRendition):
    def img_tag(self, extra_attributes):
        attrs = self.attrs_dict.copy()
        attrs.update(extra_attributes)

        if amp_mode_active():
            return mark_safe('<amp-img{}>'.format(flatatt(attrs)))
        else:
            return mark_safe('<img{}>'.format(flatatt(attrs)))

    ...

Без настраиваемой модели изображения вам придётся произвести monkey-patch встроенной модели Rendition. Добавьте это в любой части вашего проекта, где оно будет импортировано при запуске:

from django.forms.utils import flatatt
from django.utils.safestring import mark_safe

from wagtail.images.models import Rendition

def img_tag(rendition, extra_attributes={}):
    """
    Replacement implementation for Rendition.img_tag

    When AMP mode is on, this returns an <amp-img> tag instead of an <img> tag
    """
    attrs = rendition.attrs_dict.copy()
    attrs.update(extra_attributes)

    if amp_mode_active():
        return mark_safe('<amp-img{}>'.format(flatatt(attrs)))
    else:
        return mark_safe('<img{}>'.format(flatatt(attrs)))

Rendition.img_tag = img_tag

© 2014-present Torchbox Ltd and individual contributors.
All rights are reserved.
Licensed under the BSD License.
https://docs.wagtail.org/en/v3.0.3/advanced_topics/amp.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API