Marc LaBelle
Marc LaBelle

Reputation: 320

How can you add a many to many field in Wagtail Admin?

I am trying to set make a footer for a Wagtail site that is included on every page, but I want to include a list of links (phone, email, social media). If I try the code below without the panel = [...] I can see it sort of works, but I am unable to add any items:

enter image description here

from wagtail.contrib.settings.models import BaseSetting, register_setting
from django import forms

class ContactInfo(models.Model):
    CONTACT_CHOICES = (
        ('fas fa-phone', 'Phone'),
        ('fas fa-envelope', 'Email'),
        ('fab fa-facebook-f', 'Facebook'),
        ('fa-instagram', 'Instagram'),
        ('fab fa-linkedin', 'LinkedIn'),
        ('fab fa-twitter', 'Twitter'),
        ('fab fa-pinterest', 'Pinterest'),
        ('fab fa-github', 'GitHub'),
        ('fab fa-gitlab', 'GitLab'),
    )

    contact_type = models.CharField(choices=CONTACT_CHOICES, max_length=50)
    contact_info = models.CharField(max_length=50)
    info_prefix = models.CharField(max_length=10, editable=False)

    def save(self, *args, **kwargs):
        if self.contact_type == 'Phone':
            self.info_prefix = 'tel:'
        elif self.contact_type == 'Email':
            self.info_prefix = 'mailto:'
        else:
            self.info_prefix = ''


@register_setting
class Contact(BaseSetting):
    contact = models.ManyToManyField(ContactInfo)

    panels = [
        FieldPanel('contact', widget=forms.CheckboxSelectMultiple)
    ]

Is there a to add items to the M2M field? Is there a way to make lists of items in the Wagtail settings? Is there an easier way to make a footer that automatically is rendered on every page?

Upvotes: 1

Views: 3513

Answers (1)

gasman
gasman

Reputation: 25292

Each ContactInfo item (presumably) belongs to a single Contact, so this is a one-to-many relation rather than many-to-many. (A many-to-many relation in this case would mean that you have a shared pool of ContactInfo items previously defined through some other view, and you're selecting which ones to attach to the current Contact.)

In Wagtail, this would be defined using a ParentalKey on ContactInfo to point to the corresponding Contact, and rendered with an InlinePanel. (See the gallery image example from the Wagtail tutorial for an example.)

from django.db import models
from wagtail.admin.edit_handlers import FieldPanel, InlinePanel
from wagtail.core.models import Orderable
from wagtail.contrib.settings.models import BaseSetting, register_setting
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel

class ContactInfo(Orderable):
    CONTACT_CHOICES = (
        # ...
    )

    contact = ParentalKey('Contact', on_delete=models.CASCADE, related_name='contact_links')
    contact_type = models.CharField(choices=CONTACT_CHOICES, max_length=50)
    contact_info = models.CharField(max_length=50)
    # info_prefix handling omitted here for brevity

    panels = [
        FieldPanel('contact_type'),
        FieldPanel('contact_info'),
    ]


@register_setting
class Contact(BaseSetting, ClusterableModel):
    panels = [
        InlinePanel('contact_links', label="Contact")
    ]

Upvotes: 6

Related Questions