Типы страниц
Каждый тип страницы (также известный как тип контента) в Wagtail представлен моделью Django. Все модели страниц должны наследоваться от класса wagtail.core.models.Page.
Поскольку все типы страниц являются моделями Django, вы можете использовать любой тип поля, предоставляемый Django. Полный список типов полей см. в Справочнике по полям моделей. Wagtail также предоставляет wagtail.core.fields.RichTextField для редактирования текстового контента в формате WYSIWYG.
Примечание
Если вы ещё не знакомы с моделями Django, ознакомьтесь со следующими ссылками, чтобы начать:
Пример модели страницы Wagtail
Этот пример представляет типичную запись блога:
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.core.models import Page, Orderable
from wagtail.core.fields import RichTextField
from wagtail.admin.edit_handlers import FieldPanel, MultiFieldPanel, InlinePanel
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.search import index
class BlogPage(Page):
# Database fields
body = RichTextField()
date = models.DateField("Post date")
feed_image = models.ForeignKey(
'wagtailimages.Image',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
# Search index configuration
search_fields = Page.search_fields + [
index.SearchField('body'),
index.FilterField('date'),
]
# Editor panels configuration
content_panels = Page.content_panels + [
FieldPanel('date'),
FieldPanel('body', classname="full"),
InlinePanel('related_links', label="Related links"),
]
promote_panels = [
MultiFieldPanel(Page.promote_panels, "Common page configuration"),
ImageChooserPanel('feed_image'),
]
# Parent page / subpage type rules
parent_page_types = ['blog.BlogIndex']
subpage_types = []
class BlogPageRelatedLink(Orderable):
page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')
name = models.CharField(max_length=255)
url = models.URLField()
panels = [
FieldPanel('name'),
FieldPanel('url'),
]
Примечание
Убедитесь, что имена ваших полей не совпадают с именами ваших классов. Это вызовет ошибки из-за того, как Django обрабатывает связи (подробнее). В наших примерах мы избежали этого, добавив «Page» к каждому имени модели.
Создание моделей страниц
Здесь мы опишем каждый раздел вышеприведённого примера, чтобы помочь вам создать свои собственные модели страниц.
Поля базы данных
Каждый тип страницы Wagtail является моделью Django, представленной в базе данных как отдельная таблица.
Каждый тип страницы может иметь свой собственный набор полей. Например, новостная статья может содержать текст тела и дату публикации, в то время как страница мероприятия может потребовать отдельных полей для места проведения и времени начала/окончания.
В Wagtail вы можете использовать любой класс поля Django. Большинство классов полей, предоставляемых сторонними приложениями, также должны работать.
Wagtail также предоставляет несколько собственных классов полей:
-
RichTextField— для форматирования текста -
StreamField— поле контента на основе блоков (см.: Содержание страниц с помощью StreamField)
Для тегов Wagtail полностью поддерживает django-taggit, поэтому мы рекомендуем его использовать.
Поиск
Атрибут search_fields определяет, какие поля добавляются в индекс поиска и как они индексируются.
Это должен быть список SearchField и FilterField объектов. SearchField добавляет поле для полнотекстового поиска. FilterField добавляет поле для фильтрации результатов. Поле можно индексировать как с помощью SearchField, так и FilterField одновременно (но только по одному экземпляру каждого).
В приведённом примере мы проиндексировали body для полнотекстового поиска и date для фильтрации.
Аргументы, которые принимают эти типы полей, описаны в индексирование дополнительных полей.
Панели редактора
Существует несколько атрибутов для определения того, как поля страницы будут расположены в интерфейсе редактора страниц:
-
content_panels— для контента, такого как основной текст -
promote_panels— для метаданных, таких как теги, миниатюрное изображение и SEO-заголовок -
settings_panels— для настроек, таких как дата публикации
Каждый из этих атрибутов устанавливается в список EditHandler объектов, определяющих, какие поля отображаются на каких вкладках и как они структурированы на каждой вкладке.
Вот краткое описание EditHandler классов, которые Wagtail предоставляет из коробки. Полные описания см. в типах панелей.
Базовые
Эти классы позволяют редактировать поля модели. Класс FieldPanel выберет правильный виджет на основе типа поля, хотя для полей StreamField необходимо использовать специализированный класс панели.
Структурные
Эти классы используются для структурирования полей в интерфейсе.
Выбиратели
Для полей ForeignKey определённых моделей можно использовать один из следующих ChooserPanel классов. Они добавляют удобный интерфейс выбора в виде модального окна, а также выбиратели изображений/документов позволяют загружать новые файлы без выхода из редактора страницы.
Примечание
Для использования одного из этих выбирателей, модель, к которой осуществляется ссылка, должна быть страницей, изображением, документом или сниппетом.
Ссылка на любой другой тип модели в настоящее время не поддерживается, вам нужно использовать FieldPanel, который создаст выпадающее меню.
Настройка интерфейса редактора страниц
Интерфейс редактора страниц можно настроить более подробно. См. Настройка интерфейса редактирования.
Правила типов родительских/дочерних страниц
Эти два атрибута позволяют управлять тем, где можно использовать типы страниц на вашем сайте. Это позволяет определять правила, например, «записи блога могут создаваться только под индексом блога».
Оба принимают список классов моделей или имён моделей. Имена моделей имеют формат app_label.ModelName. Если app_label опущено, предполагается тот же модуль.
-
parent_page_typesограничивает, под какими типами страниц можно создавать этот тип -
subpage_typesограничивает, какие типы страниц могут быть созданы под этим типом
По умолчанию любой тип страницы может быть создан под любым типом страницы, и нет необходимости устанавливать эти атрибуты, если это желаемое поведение.
Установка parent_page_types в пустой список — хороший способ предотвратить создание определённого типа страниц в интерфейсе редактора.
URL-адреса страниц
Наиболее распространённый метод получения URL-адресов страниц — использование тега шаблона {% pageurl %}. Поскольку он вызывается из шаблона, pageurl автоматически включает указанные ниже оптимизации. Дополнительную информацию см. в pageurl.
Модели страниц также включают несколько методов низкого уровня для переопределения или доступа к URL-адресам страниц.
Настройка шаблонов URL-адресов для модели страницы
Метод Page.get_url_parts(request) обычно не вызывается напрямую, но может быть переопределён для определения пользовательской маршрутизации URL-адресов для заданной модели страницы. Он должен возвращать кортеж (site_id, root_url, page_path), которые используются get_url и get_full_url (см. ниже) для построения URL-адреса указанного типа страницы.
При переопределении get_url_parts() вы должны принять *args, **kwargs:
def get_url_parts(self, *args, **kwargs):
и передать их в момент вызова get_url_parts в super (если применимо), например:
super().get_url_parts(*args, **kwargs)
Хотя вы могли бы передать только ключевое слово request, передача всех аргументов как есть гарантирует совместимость с будущими изменениями сигнатур этих методов.
Дополнительную информацию см. в wagtail.core.models.Page.get_url_parts().
Получение URL-адресов для экземпляров страниц
Метод Page.get_url(request) можно вызывать всякий раз, когда требуется URL-адрес страницы. Он по умолчанию возвращает локальные URL-адреса (без протокола и домена), если определяется, что страница находится на текущем сайте (через имя хоста в request); в противном случае возвращается полный URL-адрес, включая протокол и домен. При возможности необязательный аргумент request должен быть включён для включения кеширования URL-адресов на уровне сайта на основе каждого запроса и для удобства генерации локальных URL-адресов.
Типичное применение get_url(request) — в любых пользовательских тегах шаблона, которые может включать ваш проект для генерации навигационных меню. При написании такого пользовательского тега шаблона убедитесь, что он включает takes_context=True и используйте context.get('request') для безопасной передачи запроса или None в случае отсутствия запроса в контексте.
Дополнительную информацию см. в wagtail.core.models.Page.get_url().
В случае необходимости полного URL (включая протокол и домен), можно использовать Page.get_full_url(request). В тех случаях, когда это возможно, следует включать необязательный аргумент request, чтобы включить кеширование URL-информации сайта на уровне запроса.
Для получения дополнительной информации, пожалуйста, обратитесь к wagtail.core.models.Page.get_full_url().
Отображение шаблонов
Каждый тип страницы может иметь HTML-шаблон, который отображается, когда пользователь переходит на страницу на переднем плане сайта. Это самый простой и распространённый способ отображения контента Wagtail пользователям (но не единственный).
Добавление шаблона для типа страницы
Wagtail автоматически выбирает имя шаблона на основе метки приложения и имени класса модели.
Формат: <app_label>/<model_name (snake cased)>.html
Например, шаблон для страницы блога выше будет: blog/blog_page.html
Вам просто нужно создать шаблон в расположении, где он может быть доступен с этим именем.
Контекст шаблона
Wagtail отображает шаблоны с переменной page, связанной с экземпляром страницы, которая отображается. Используйте эту переменную для доступа к содержимому страницы. Например, чтобы получить заголовок текущей страницы, используйте {{ page.title }}. Также доступны все переменные, предоставляемые обработчиками контекста.
Настройка контекста шаблона
У всех страниц есть метод get_context, который вызывается при каждом отображении шаблона и возвращает словарь переменных для связывания в шаблоне.
Чтобы добавить больше переменных в контекст шаблона, вы можете переопределить этот метод:
class BlogIndexPage(Page):
...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
# Add extra variables and return the updated context
context['blog_entries'] = BlogPage.objects.child_of(self).live()
return context
Переменные затем можно использовать в шаблоне:
{{ page.title }}
{% for entry in blog_entries %}
{{ entry.title }}
{% endfor %}
Изменение шаблона
Установите атрибут template в классе, чтобы использовать другой файл шаблона:
class BlogPage(Page):
...
template = 'other_template.html'
Динамический выбор шаблона
Шаблон можно изменить на основе экземпляра, определив метод get_template в классе страницы. Этот метод вызывается каждый раз, когда страница отображается:
class BlogPage(Page):
...
use_other_template = models.BooleanField()
def get_template(self, request, *args, **kwargs):
if self.use_other_template:
return 'blog/other_blog_page.html'
return 'blog/blog_page.html'
В этом примере страницы, у которых поле use_other_template типа boolean установлено, будут использовать шаблон blog/other_blog_page.html. Все остальные страницы будут использовать стандартный шаблон blog/blog_page.html.
Шаблоны AJAX
Если вы хотите добавить функциональность AJAX на страницу, например, постраничный список, который обновляется на месте на странице, а не вызывает полную перезагрузку страницы, вы можете установить атрибут ajax_template для указания альтернативного шаблона, который будет использоваться, когда страница запрашивается через AJAX-вызов (как указано заголовком X-Requested-With: XMLHttpRequest HTTP):
class BlogPage(Page):
...
ajax_template = 'other_template_fragment.html'
template = 'other_template.html'
Больший контроль над отображением страницы
Все классы страниц имеют метод serve(), который внутренне вызывает методы get_context и get_template и отображает шаблон. Этот метод похож на функцию представления Django, принимая объект Django Request и возвращая объект Django Response.
Этот метод также может быть переопределён для полного контроля над отображением страницы.
Например, вот способ сделать так, чтобы страница отвечала JSON-представлением самой себя:
from django.http import JsonResponse
class BlogPage(Page):
...
def serve(self, request):
return JsonResponse({
'title': self.title,
'body': self.body,
'date': self.date,
# Resizes the image to 300px width and gets a URL to it
'feed_image': self.feed_image.get_rendition('width-300').url,
})
Вложенные модели
Wagtail может вкладывать содержимое других моделей в страницу. Это полезно для создания повторяющихся полей, таких как связанные ссылки или элементы для отображения в карусели. Контент вложенных моделей также версионируется вместе с остальным контентом страницы.
Каждая вложенная модель требует следующего:
- Она должна наследоваться от
wagtail.core.models.Orderable - Она должна иметь
ParentalKeyк родительской модели
Примечание
django-modelcluster и ParentalKey
Функция встраивания моделей предоставляется django-modelcluster, и тип поля ParentalKey должен быть импортирован оттуда:
from modelcluster.fields import ParentalKey
ParentalKey является подклассом Django ForeignKey, и принимает те же аргументы.
Например, следующая вложенная модель может быть использована для добавления связанных ссылок (список пар «название», «url») к модели BlogPage:
from django.db import models
from modelcluster.fields import ParentalKey
from wagtail.core.models import Orderable
class BlogPageRelatedLink(Orderable):
page = ParentalKey(BlogPage, on_delete=models.CASCADE, related_name='related_links')
name = models.CharField(max_length=255)
url = models.URLField()
panels = [
FieldPanel('name'),
FieldPanel('url'),
]
Чтобы добавить это в интерфейс администратора, используйте класс панели редактирования InlinePanel:
content_panels = [
...
InlinePanel('related_links', label="Related links"),
]
Первый аргумент должен совпадать со значением атрибута related_name модели ParentalKey.
Работа со страницами
Wagtail использует функцию многотабличного наследования Django, чтобы разрешить использование нескольких моделей страниц в одном дереве.
Каждая страница добавляется как в встроенную модель Wagtail Page, так и в определённую пользователем модель (например, модель BlogPage созданную ранее).
Страницы могут существовать в коде Python в двух формах: экземпляр Page или экземпляр модели страницы.
При работе с несколькими типами страниц вместе, вы обычно используете экземпляры модели Wagtail Page, которые не дают вам доступа к полям, специфичным для их типа.
# Get all pages in the database >>> from wagtail.core.models import Page >>> Page.objects.all() [<Page: Homepage>, <Page: About us>, <Page: Blog>, <Page: A Blog post>, <Page: Another Blog post>]
При работе с одним типом страницы, вы можете работать с экземплярами пользовательской модели. Они предоставляют доступ ко всем полям, доступным в Page, а также любые пользовательские поля для этого типа.
# Get all blog entries in the database >>> BlogPage.objects.all() [<BlogPage: A Blog post>, <BlogPage: Another Blog post>]
Вы можете преобразовать объект Page в его более специфичный пользовательский эквивалент, используя свойство .specific. Это может привести к дополнительной выборке из базы данных.
>>> page = Page.objects.get(title="A Blog post") >>> page <Page: A Blog post> # Note: the blog post is an instance of Page so we cannot access body, date or feed_image >>> page.specific <BlogPage: A Blog post>
Рекомендации
Дружественные имена моделей
Вы можете сделать имена ваших моделей более удобными для пользователей Wagtail, используя внутренний класс Django Meta с verbose_name, например:
class HomePage(Page):
...
class Meta:
verbose_name = "homepage"
Когда пользователям предлагается выбор страниц для создания, список типов страниц генерируется путём разделения имён моделей по заглавным буквам. Таким образом, модель HomePage будет названа «Главная страница», что немного неудобно. Определение verbose_name как в примере выше, изменит это на «Главная», что немного более стандартно.
Порядок запросов страниц
Модели, наследуемые от Page, не могут получить порядок по умолчанию, используя стандартный подход Django – добавление атрибута ordering к внутренней модели Meta.
class NewsItemPage(Page):
publication_date = models.DateField()
...
class Meta:
ordering = ('-publication_date', ) # will not work
Это связано с тем, что Page применяет порядок запросов по пути. Вместо этого необходимо явно задать порядок при построении набора запросов:
news_items = NewsItemPage.objects.live().order_by('-publication_date')
Пользовательские менеджеры страниц
Вы можете добавить пользовательского менеджера Manager к вашему классу Page. Любые пользовательские менеджеры должны наследоваться от wagtail.core.models.PageManager:
from django.db import models
from wagtail.core.models import Page, PageManager
class EventPageManager(PageManager):
""" Custom manager for Event pages """
class EventPage(Page):
start_date = models.DateField()
objects = EventPageManager()
Альтернативно, если вам нужно добавить только дополнительные методы QuerySet, вы можете унаследовать от wagtail.core.models.PageQuerySet для построения пользовательского менеджера Manager:
from django.db import models
from django.utils import timezone
from wagtail.core.models import Page, PageManager, PageQuerySet
class EventPageQuerySet(PageQuerySet):
def future(self):
today = timezone.localtime(timezone.now()).date()
return self.filter(start_date__gte=today)
EventPageManager = PageManager.from_queryset(EventPageQuerySet)
class EventPage(Page):
start_date = models.DateField()
objects = EventPageManager()
© 2014-present Torchbox Ltd and individual contributors.
All rights are reserved.
Licensed under the BSD License.
https://docs.wagtail.org/en/v2.16.3/topics/pages.html