Как создать сайт с поддержкой AMP
В этом документе описывается метод создания версии сайта Wagtail с поддержкой AMP и размещения ее отдельно от остальной части сайта по префиксу URL. Также описывается, как заставить Wagtail отображать изображения с тегом <amp-img> при посещении страницы AMP-версии сайта пользователем.
Обзор
В следующем разделе мы добавим новую запись URL, которая указывает на внутренний serve() вид Wagtail, что приведет к повторному отображению всего сайта по префиксу /amp.
Затем мы добавим некоторые утилиты, которые позволят отслеживать, находится ли текущий запрос в префиксной версии сайта /amp без необходимости объекта запроса.
После этого мы добавим обработчик контекста шаблона, чтобы проверить изнутри шаблонов, какая версия сайта отображается.
И наконец, мы изменим поведение тега {% image %}, чтобы при отрисовке AMP-версии сайта он отображал теги <amp-img>.
Создание второго дерева страниц
Мы можем отображать весь сайт по другому префиксу, дублируя 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, который использовался для отображения страницы. Для этого нам нужно обернуть 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— это менеджер контекста, который можно вызвать с помощью синтаксиса Pythonwith. В теле оператораwithрежим AMP будет активным. -
amp_mode_active— это функция, которая возвращаетTrueкогда активен режим AMP.
Далее, нам нужно определить вид, который оборачивает встроенный serve вид Wagtail и вызывает менеджер контекста activate_amp_mode.
# <app>/amp_views.py
from django.template.response import SimpleTemplateResponse
from wagtail.core.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.core.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'
Переопределение тега изображения для вывода тегов amp-img
Наконец, давайте изменим тег изображения Wagtail, чтобы он отображал теги <amp-img> при отображении страниц с включенным AMP. Мы внесем изменения в модель Rendition, чтобы это применилось как к изображениям, отображаемым с помощью тега {% image %}, так и к изображениям, отображаемым в полях богатого текста.
С собственной моделью изображений это проще, поскольку вы можете переопределить метод 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/v2.16.3/advanced_topics/amp.html