Learn Wagtail Course

Using a SnippetChooserPanel to Select Multiple Blog Authors (Snippets + Orderables)

This is part 2 about registering Snippets and using them in a SnippetChooserPanel. If you didn't catch the first video, you can find it here.

In this video we take an existing Snippet and let the user select the Snippet from the Wagtail Admin. But we take this a step further and allow the user to select between one and four Blog Authors by using a Orderable (more on Orderables here).

The entire process goes like this:

  1. Create a Django Model
  2. Register the model as a Wagtail Snippet
  3. Create an Orderable with a ForeignKey to the Django Model
  4. Create a SnippetChooserPanel that links to the ForeignKey
  5. On your page add an InlinePanel that links to your Orderable's related_name.

That's just about all there is to it. The code can be kind of confusing if this is your first time, so please take a look at the Git Commit or the chunk of code below.

The Code

# blog.models.py 
"""Blog listing and blog detail pages."""
from django.db import models

from modelcluster.fields import ParentalKey
from wagtail.admin.edit_handlers import (
    FieldPanel,
    MultiFieldPanel,
    InlinePanel,
)
from wagtail.snippets.edit_handlers import SnippetChooserPanel
from wagtail.core.models import Page, Orderable
from wagtail.images.edit_handlers import ImageChooserPanel
from wagtail.snippets.models import register_snippet


class BlogAuthorsOrderable(Orderable):
    """This allows us to select one or more blog authors from Snippets."""

    page = ParentalKey("blog.BlogDetailPage", related_name="blog_authors")
    author = models.ForeignKey(
        "blog.BlogAuthor",
        on_delete=models.CASCADE,
    )

    panels = [
    	# Use a SnippetChooserPanel because blog.BlogAuthor is registered as a snippet
        SnippetChooserPanel("author"),
    ]


@register_snippet
class BlogAuthor(models.Model):
    """Blog author for snippets."""

    name = models.CharField(max_length=100)
    website = models.URLField(blank=True, null=True)
    image = models.ForeignKey(
        "wagtailimages.Image",
        on_delete=models.SET_NULL,
        null=True,
        blank=False,
        related_name="+",
    )

    panels = [
        MultiFieldPanel(
            [
                FieldPanel("name"),
                # Use an ImageChooserPanel because wagtailimages.Image (image property) 
                # is a ForeignKey to an Image
                ImageChooserPanel("image"),
            ],
            heading="Name and Image",
        ),
        MultiFieldPanel(
            [
                FieldPanel("website"),
            ],
            heading="Links"
        )
    ]

    def __str__(self):
        """String repr of this class."""
        return self.name

    class Meta:  # noqa
        verbose_name = "Blog Author"
        verbose_name_plural = "Blog Authors"


class BlogDetailPage(Page):
    """Blog detail page."""

   # ...

    content_panels = Page.content_panels + [
    	# ...
        MultiFieldPanel(
            [
                InlinePanel("blog_authors", label="Author", min_num=1, max_num=4)
            ],
            heading="Author(s)"
        ),
        # ...
    ]
{# blog_detail_page.html code snippet #}

{# Loop through all blog authors #}
{% for iter in self.blog_authors.all %}
    {% image iter.author.image fill-50x50 as img %}
    <div>
        <img src="{{ img.url }}" class="rounded-circle" alt="{{ iter.author.name }}">
    </div>
    {% if iter.author.website %}
        {# If there's a website, create an <a> tag
        <a href="{{ iter.author.website }}">
            {{ iter.author.name }}
        </a>
    {% else %}
        {# No website; no <a> tag #}
        {{ iter.author.name }}
    {% endif %}
    </div>
{% endfor %}

The Git Commit

Just want the raw code? No problem. Follow this link to the Git Commit to see everything that was changed.

Was this helpful to you?

Sharing is caring. Help the community by sharing this article.