Розробка користувацьких Snippets Wagtail
Snippets в Wagtail — це Django-моделі, зареєстровані в адміністративній панелі як керовані об'єкти контенту. На відміну від сторінок, сніпети не мають URL і не беруть участь у дереві сайту. Їх використовують для переіспользуємих фрагментів: меню, баннерів, контактних даних, команди, партнерів, відзивів — всього, що потрібно в різних місцях сайту незалежно від структури сторінок.
Реєстрація сніпету
# models.py
from django.db import models
from wagtail.snippets.models import register_snippet
from wagtail.admin.panels import FieldPanel, MultiFieldPanel
from wagtail.fields import RichTextField
@register_snippet
class Testimonial(models.Model):
author_name = models.CharField('Ім\'я', max_length=100)
author_title = models.CharField('Должність', max_length=100, blank=True)
author_photo = models.ForeignKey(
'wagtailimages.Image',
null=True, blank=True,
on_delete=models.SET_NULL,
related_name='+'
)
text = RichTextField('Текст відзиву', features=['bold', 'italic'])
rating = models.PositiveSmallIntegerField('Оцінка', default=5)
is_featured = models.BooleanField('Показувати на головній', default=False)
panels = [
MultiFieldPanel([
FieldPanel('author_name'),
FieldPanel('author_title'),
FieldPanel('author_photo'),
], heading='Автор'),
FieldPanel('text'),
FieldPanel('rating'),
FieldPanel('is_featured'),
]
def __str__(self):
return f'{self.author_name} — {self.rating}★'
class Meta:
verbose_name = 'Відзив'
verbose_name_plural = 'Відзиви'
ordering = ['-is_featured', '-id']
ViewSetGroup для групування в меню
Починаючи з Wagtail 4.1 сніпети реєструються через SnippetViewSet — це дає повний контроль над поведінкою в админці:
from wagtail.snippets.views.snippets import SnippetViewSet, SnippetViewSetGroup
from wagtail.snippets.models import register_snippet
class TestimonialViewSet(SnippetViewSet):
model = Testimonial
icon = 'comment'
menu_label = 'Відзиви'
menu_name = 'testimonials'
list_display = ['author_name', 'author_title', 'rating', 'is_featured']
list_filter = ['is_featured', 'rating']
search_fields = ['author_name', 'text']
ordering = ['-is_featured', '-id']
class TeamMemberViewSet(SnippetViewSet):
model = TeamMember
icon = 'user'
menu_label = 'Команда'
menu_name = 'team'
list_display = ['full_name', 'position', 'department']
list_filter = ['department']
class ContentSnippetsGroup(SnippetViewSetGroup):
menu_label = 'Контент'
menu_icon = 'folder-open-inverse'
menu_order = 300
items = [TestimonialViewSet, TeamMemberViewSet]
register_snippet(ContentSnippetsGroup)
Сніпет з історією версій
from wagtail.models import DraftStateMixin, RevisionMixin, PreviewableMixin
from wagtail.admin.panels import PublishingPanel
@register_snippet
class SiteAnnouncement(DraftStateMixin, RevisionMixin, PreviewableMixin, models.Model):
title = models.CharField(max_length=200)
body = RichTextField()
show_from = models.DateTimeField(null=True, blank=True)
show_until = models.DateTimeField(null=True, blank=True)
is_dismissible = models.BooleanField(default=True)
panels = [
FieldPanel('title'),
FieldPanel('body'),
FieldPanel('show_from'),
FieldPanel('show_until'),
FieldPanel('is_dismissible'),
PublishingPanel(),
]
def get_preview_template(self, request, mode_name):
return 'previews/announcement.html'
def __str__(self):
return self.title
class Meta:
verbose_name = 'Оголошення'
verbose_name_plural = 'Оголошення'
DraftStateMixin додає чорновики та публікацію. RevisionMixin — історію версій з можливістю відкату. PreviewableMixin — кнопку предпросмотру в редакторі.
Використання сніпетів в StreamField
from wagtail.snippets.blocks import SnippetChooserBlock
from wagtail.blocks import StructBlock, ListBlock
class TestimonialsBlock(StructBlock):
heading = CharBlock(max_length=100, required=False)
items = ListBlock(SnippetChooserBlock('content.Testimonial'))
class Meta:
label = 'Блок відзивів'
template = 'blocks/testimonials.html'
В шаблоні блоку дані сніпету доступні напрямки:
{# blocks/testimonials.html #}
<section class="testimonials">
{% if value.heading %}<h2>{{ value.heading }}</h2>{% endif %}
<div class="testimonials__grid">
{% for item in value.items %}
{% include "snippets/testimonial_card.html" with testimonial=item %}
{% endfor %}
</div>
</section>
Сніпет як глобальні налаштування
Частий паттерн — сніпет для налаштувань, які редактор рідко міняє, але які потрібні на всіх сторінках: контакти, соцмережі, SEO-умолчання:
from wagtail.contrib.settings.models import BaseSiteSetting, register_setting
@register_setting
class SiteSettings(BaseSiteSetting):
phone = models.CharField('Телефон', max_length=30, blank=True)
email = models.EmailField('Email', blank=True)
address = models.TextField('Адреса', blank=True)
vk_url = models.URLField('ВКонтакте', blank=True)
telegram_url = models.URLField('Telegram', blank=True)
google_analytics_id = models.CharField('Google Analytics ID', max_length=20, blank=True)
panels = [
MultiFieldPanel([
FieldPanel('phone'),
FieldPanel('email'),
FieldPanel('address'),
], heading='Контакти'),
MultiFieldPanel([
FieldPanel('vk_url'),
FieldPanel('telegram_url'),
], heading='Соцмережі'),
FieldPanel('google_analytics_id'),
]
class Meta:
verbose_name = 'Налаштування сайту'
В шаблонах доступно через тег {% get_settings %}:
{% load wagtailsettings_tags %}
{% get_settings %}
<a href="tel:{{ settings.website.SiteSettings.phone }}">
{{ settings.website.SiteSettings.phone }}
</a>
API для headless
Якщо сайт використовує Wagtail API, сніпети потрібно явно зареєструвати в API:
# api.py
from wagtail.api.v2.router import WagtailAPIRouter
from wagtail.api.v2.views import BaseAPIViewSet
from .models import Testimonial
api_router = WagtailAPIRouter('wagtailapi')
class TestimonialsAPIViewSet(BaseAPIViewSet):
model = Testimonial
body_fields = BaseAPIViewSet.body_fields + [
'author_name', 'author_title', 'text', 'rating',
]
listing_default_fields = BaseAPIViewSet.listing_default_fields + [
'author_name', 'rating', 'is_featured',
]
filter_fields = ['is_featured']
api_router.register_endpoint('testimonials', TestimonialsAPIViewSet)
Запит повертає структурований JSON:
GET /api/v2/testimonials/?is_featured=true&order=-rating
Часові рамки
Один сніпет з панелями, пошуком та фільтрацією — 2–3 години. Комплект з 5–7 сніпетів для типового корпоративного сайту (команда, партнери, відзиви, налаштування, меню, баннери) з налаштуванням ViewSetGroup та API-доступом — 2–3 дні.







