Skip to content

Instantly share code, notes, and snippets.

@alexgleason
Last active May 9, 2023 17:49
Show Gist options
  • Save alexgleason/09b1802b004babd74e96 to your computer and use it in GitHub Desktop.
Save alexgleason/09b1802b004babd74e96 to your computer and use it in GitHub Desktop.
Many to many relationships in Wagtail
from django.db import models
from wagtail.wagtailsnippets.models import register_snippet
from wagtail.wagtailcore.models import Page
from modelcluster.fields import ParentalKey
from wagtail.wagtailadmin.edit_handlers import InlinePanel
@register_snippet
class Category(models.Model):
"""
There can be many categories related to many posts.
"""
title = models.CharField(max_length=255)
class Post(Page):
"""
There can be many posts related to many categories.
"""
# Categories come from a different table, but it's useful to have
# a way to retrieve them easily in the correct format.
@property
def categories(self):
categories = [
# Grabs rows from the relationship index and formats them correctly.
# See the `PostCategoryRelationship` class below.
n.category for n in self.post_category_relationship.all()
]
return categories
# Only inline panels work here.
content_panels = Page.content_panels + [
# This name comes from the "related_name" field explained below.
InlinePanel('post_category_relationship')
]
class PostCategoryRelationship(models.Model):
"""
An index of references between posts and categories.
This is where the relationship is defined.
Ex. a post with three categories will create three rows in this table.
For instance, a post titled "Bitcoin is dead" in the categories
"tech", "finance", and "life" will create the following rows:
row = 1
post = "Bitcoin is dead"
category = "tech"
row = 2
post = "Bitcoin is dead"
category = "finance"
row = 3
post = "Bitcoin is dead"
category = "life"
"""
# The post this relationship is for.
# ParentalKey is just like ForeignKey, but needed for Wagtail's "preview"
# function to work. More: https://github.com/torchbox/django-modelcluster
post = ParentalKey(
'Post',
related_name='post_category_relationship'
)
# The "related name" allows us to grab rows from this table with a post.
# It creates a new property on all posts that you can access like so:
#
# my_post.post_category_relationship
# The category related to this post
category = models.ForeignKey(
'Category',
related_name="+"
)
panels = [
FieldPanel('category')
]
from django.db import models
from wagtail.wagtailsnippets.models import register_snippet
from wagtail.wagtailcore.models import Page
from modelcluster.fields import ParentalKey
from wagtail.wagtailadmin.edit_handlers import InlinePanel
@register_snippet
class Category(models.Model):
title = models.CharField(max_length=255)
class Post(Page):
@property
def categories(self):
categories = [
n.category for n in self.post_category_relationship.all()
]
return categories
content_panels = Page.content_panels + [
InlinePanel('post_category_relationship')
]
class PostCategoryRelationship(models.Model):
post = ParentalKey(
'Post',
related_name='post_category_relationship'
)
category = models.ForeignKey(
'Category',
related_name="+"
)
panels = [
FieldPanel('category')
]
{% if page.categories %}
<ul class="categories">
{% for category in page.categories %}
<li>{{ category.title }}</li>
{% endfor %}
</ul>
{% endif %}
@HyunWinter
Copy link

Thank you for sharing this, but isn't this an one to many relationship? How can we reference it from Category?

@alexgleason
Copy link
Author

This gist is from around 5 years ago. I think Wagtail now has native support for Django's ManyToMany field.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment