Spec-Zone.ru › Wagtail

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

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

Обзор

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

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

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

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

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

Мы можем отображать весь сайт по другому префиксу, дублируя Wagtail URL в файле проекта 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, который использовался для рендеринга страницы. Для этого мы должны обернуть serve() вид Wagtail и установить локальную переменную потока, чтобы указать всем последующим частям кода, что режим 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.

Далее, нам нужно определить представление, которое обертывает встроенный serve вид Wagtail и вызывает 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>

Наконец, давайте изменим тэг {% image %} Wagtail, чтобы он рендерил тэги <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)))

    ...

Без настраиваемой модели изображений вам придется подменить встроенную модель 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/stable/advanced_topics/amp.html

Spec-Zone.ru

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