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

Когда стандартных возможностей Wagtail не хватает для нестандартных страниц, мы разрабатываем кастомные Page Models. Наша команда создаёт поля, панели и маршруты под ваши задачи, реализуя проект под ключ — от концепции до поддержки. В результате вы получаете удобную админку и гибкое решение, которое масштабируется вместе с бизнесом.

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

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

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

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

Типичная ситуация: клиент просит не просто страницу «О нас», а целый каталог услуг с ценами, тарифами и фильтрацией. Готовые блоки не подходят — нужны свои поля, вкладки в админке и своя маршрутизация. Решение — кастомные 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 решат вашу задачу.

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

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

  • Разработка сайта компании B2B ADVANCE
    Разработка сайта компании B2B ADVANCE
    1542
  • Разработка веб-приложения для компании FEEDME
    Разработка веб-приложения для компании FEEDME
    1393
  • Разработка веб-сайта для компании БЕЛФИНГРУПП
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    1081
  • Разработка интернет магазина для компании FURNORO
    Разработка интернет магазина для компании FURNORO
    1368
  • Разработка веб-приложения для компании Enviok
    Разработка веб-приложения для компании Enviok
    1139
  • Разработка веб-сайта для компании ФИКСПЕР
    Разработка веб-сайта для компании ФИКСПЕР
    1060