Разработка кастомных Page Models Wagtail: поля, панели, маршруты

Типичная ситуация: клиент просит не просто страницу «О нас», а целый каталог услуг с ценами, тарифами и фильтрацией. Готовые блоки не подходят — нужны свои поля, вкладки в админке и своя маршрутизация. Решение — кастомные Page Models. Мы используем этот подход в каждом проекте на Wagtail уже более 5

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Услуги, которые мы предлагаем
Показано 1 из 1Все 2062 услуг
Разработка кастомных Page Models Wagtail: поля, панели, маршруты
Средний
~2-3 дня

Наши компетенции:

Часто задаваемые вопросы

Последние работы

  • image_website-b2b-advance_0.webp
    Разработка сайта компании B2B ADVANCE
    1414
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1285
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    980
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1240
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    982
  • image_bitrix-bitrix-24-1c_fixper_448_0.webp
    Разработка веб-сайта для компании ФИКСПЕР
    994

Типичная ситуация: клиент просит не просто страницу «О нас», а целый каталог услуг с ценами, тарифами и фильтрацией. Готовые блоки не подходят — нужны свои поля, вкладки в админке и своя маршрутизация. Решение — кастомные Page Models. Мы используем этот подход в каждом проекте на Wagtail уже более 5 лет и накопили более 30 успешных кейсов. Например, один интернет-магазин потребовал 7 различных типов страниц с уникальными полями и связями — вся разработка заняла 4 дня, а редакторы получили понятный интерфейс с вкладками. В среднем сложный проект требует 5–10 типов страниц. Стандартные поля Wagtail (заголовок, slug, тело) не покрывают потребностей: если втиснуть тарифы, характеристики и отзывы в одно RichTextField, админка превращается в кошмар. На одном проекте редакторы тратили по 20 минут на редактирование одной страницы — после внедрения кастомных моделей время сократилось до 2 минут. Снижение на 90%.

Как кастомные Page Models упрощают жизнь редактору?

Редактору нужна понятная админка. Для этого мы используем TabbedInterface с объектами ObjectList. В примере ниже — модель ServicePage с тремя вкладками. Обратите внимание на InlinePanel для связанных записей и на pricing_panels для отображения цен.

from django.db import models from wagtail.models import Page, Orderable from wagtail.fields import RichTextField, StreamField from wagtail.admin.panels import FieldPanel, InlinePanel, MultiFieldPanel, TabbedInterface, ObjectList from wagtail.search import index from modelcluster.fields import ParentalKey class ServicePage(Page): intro = models.CharField(max_length=500, blank=True) body = RichTextField(blank=True) icon = models.ForeignKey( 'wagtailimages.Image', null=True, blank=True, on_delete=models.SET_NULL, related_name='+', ) price_from = models.DecimalField(max_digits=10, decimal_places=0, null=True, blank=True) search_fields = Page.search_fields + [ index.SearchField('intro'), index.SearchField('body'), index.FilterField('price_from'), ] content_panels = Page.content_panels + [ FieldPanel('intro'), FieldPanel('icon'), FieldPanel('body'), InlinePanel('features', label='Особенности'), ] promote_panels = [ MultiFieldPanel([ FieldPanel('slug'), FieldPanel('seo_title'), FieldPanel('search_description'), ], heading='SEO'), ] pricing_panels = [ FieldPanel('price_from'), InlinePanel('pricing_tiers', label='Тарифы'), ] edit_handler = TabbedInterface([ ObjectList(content_panels, heading='Содержание'), ObjectList(pricing_panels, heading='Цены'), ObjectList(promote_panels, heading='SEO'), ]) parent_page_types = ['services.ServicesIndexPage'] subpage_types = [] class Meta: verbose_name = 'Service Page' class ServiceFeature(Orderable): page = ParentalKey(ServicePage, on_delete=models.CASCADE, related_name='features') title = models.CharField(max_length=255) text = models.TextField(blank=True) icon = models.CharField(max_length=50, blank=True) panels = [ FieldPanel('title'), FieldPanel('text'), FieldPanel('icon'), ] 

Почему RoutablePage лучше отдельных страниц?

Если нужно собрать блог с категориями и тегами, вы можете создать отдельные Page Model для каждой сущности, а можете использовать RoutablePage. Сравните: при наполнении 10 категориями и 20 тегами отдельные страницы потребуют 30 моделей. Одна BlogIndexPage с @path декораторами заменяет их все, сокращая код в 2 раза и упрощая поддержку. Согласно документации Wagtail, RoutablePage позволяет связывать одну страницу с несколькими URL-представлениями.

from wagtail.contrib.routable_page.models import RoutablePage, path, re_path class BlogIndexPage(RoutablePage, Page): intro = RichTextField(blank=True) content_panels = Page.content_panels + [FieldPanel('intro')] subpage_types = ['blog.BlogPost'] @path('') def index_view(self, request): posts = BlogPost.objects.live().child_of(self).order_by('-first_published_at') from django.core.paginator import Paginator paginator = Paginator(posts, 12) return self.render(request, context_overrides={ 'posts': paginator.get_page(request.GET.get('page')), }) @path('category/<str:category_slug>/') def category_view(self, request, category_slug): category = get_object_or_404(BlogCategory, slug=category_slug) posts = BlogPost.objects.live().child_of(self).filter(categories=category) return self.render(request, context_overrides={ 'posts': posts, 'category': category, }, template='blog/blog_category.html') @path('tag/<str:tag>/') def tag_view(self, request, tag): posts = BlogPost.objects.live().child_of(self).filter(tags__slug=tag) return self.render(request, context_overrides={ 'posts': posts, 'tag': tag, }) @re_path(r'^archive/(\d{4})/$') def year_archive(self, request, year): posts = BlogPost.objects.live().child_of(self).filter(date__year=year) return self.render(request, context_overrides={ 'posts': posts, 'year': year, }) 

Как кастомизировать изображения?

Для соответствия SEO и accessibility мы расширяем стандартную модель Image, добавляя поля alt_text и credit. Это позволяет редактору заполнять обязательный alt-текст и указывать автора снимка.

# core/models.py from wagtail.images.models import AbstractImage, AbstractRendition, Image class CustomImage(AbstractImage): alt_text = models.CharField(max_length=255, blank=True) credit = models.CharField(max_length=255, blank=True) admin_form_fields = Image.admin_form_fields + [ 'alt_text', 'credit', ] @property def default_alt_text(self): return self.alt_text or self.title class CustomRendition(AbstractRendition): image = models.ForeignKey(CustomImage, on_delete=models.CASCADE, related_name='renditions') class Meta: unique_together = [('image', 'filter_spec', 'focal_point_key')] 

get_context — передача дополнительных данных

Метод get_context позволяет обогатить шаблон данными из других таблиц, GET-параметров или форм. Он вызывается при рендеринге страницы.

def get_context(self, request): context = super().get_context(request) context['related_posts'] = BlogPost.objects.live().exclude( pk=self.pk ).filter( categories__in=self.categories.all() ).distinct().order_by('-first_published_at')[:3] context['comment_form'] = CommentForm() context['current_tag'] = request.GET.get('tag') return context 

Сравнение подходов к построению Page Models

Подход Применение Сложность Скорость поддержки
Базовые поля Page Простые страницы (About, Contact) Низкая Высокая
Кастомные поля + панели Страницы услуг, каталоги Средняя Средняя
StreamField Гибкий лендинг, редактор Средняя Низкая
RoutablePage Блоги, разделы с фильтрацией Высокая Высокая
Кастомные изображения Медиа-сайты, фотоархивы Низкая Высокая
Тип поля Пример использования Админ-панель
CharField Заголовок, краткое описание FieldPanel
RichTextField Основной контент FieldPanel
StreamField Гибкие блоки (текст, изображения, видео) StreamFieldPanel
ForeignKey Связь с изображением или категорией FieldPanel с выбором
InlinePanel Связанные записи (тарифы, особенности) InlinePanel

В реальном проекте для интернет-магазина с каталогом из 10 000 товаров мы разработали 7 типов страниц: CatalogPage, ProductPage, BrandPage, PromotionPage, ReviewPage, FAQPage и ContactPage. Для ProductPage использовали StreamField с блоками описания, таблицы характеристик и карусели изображений. CatalogPage построили на RoutablePage с фильтрацией по категориям, цене и тегам. Разработка заняла 4 дня, редакторы обучились за час. Время публикации нового товара сократилось в 10 раз.

Процесс работы над кастомными Page Models

  1. Анализ — изучаем структуру контента клиента, определяем необходимые поля и связи.
  2. Проектирование — рисуем ER-диаграмму моделей, продумываем интерфейс редактора (вкладки, inline-панели).
  3. Реализация — пишем Django модели, настраиваем edit_handler, search_fields и RoutablePage.
  4. Тестирование — проверяем ревизии, черновики, publish workflow, производительность.
  5. Деплой — выкатываем на продакшен, настраиваем индексацию поиска.

Что входит в результат

  • Исходный код кастомных Page Models с документацией на русском.
  • Админ-панель с вкладками, валидацией полей и автодополнением.
  • Настроенная индексация для поиска по сайту.
  • Поддержка ревизий и Workflow для согласования публикации.
  • Обучение редакторов (1 час онлайн) и письменная инструкция.

Сроки и гарантии

Разработка 5–7 Page Models с RoutablePage и InlinePanel — 3–6 дней. Стоимость рассчитывается индивидуально в зависимости от сложности. Мы даём гарантию 6 месяцев на все модели: любые баги исправляем бесплатно. Такой подход позволяет сэкономить до 40% бюджета по сравнению с созданием отдельных страниц.

Оценим ваш проект за один день — просто свяжитесь. Наш опыт: более 5 лет работы с Wagtail, более 30 успешных проектов. Получите консультацию — расскажем, как кастомные Page Models решат вашу задачу.