Model with M2M Relations¶
Learn how to create flexible many-to-many relationships between your content models.
This tutorial builds on the Article with APPhooks and CMS Plugins tutorial.
Overview¶
We’ll add authors to our articles using the invite_m2m_relations feature:
Create a
Personmodel to represent authorsLink
Personobjects to articles using generic M2M relationsDisplay authors on article pages
Step 1: Create the Person Model¶
Add to my_content/models.py:
from django.db import models
from djangocms_custom_content.models import (
AbstractCustomGrouper,
AbstractCustomContent,
custom_relation_factory,
)
# Existing Article and ArticleContent models...
# (from the basic_setup tutorial)
class Person(AbstractCustomGrouper):
"""An author or contributor."""
class Meta:
verbose_name = "Author"
verbose_name_plural = "Authors"
def __str__(self):
person_content = self.get_admin_content()
return person_content.full_name if person_content else "Unknown"
class PersonContent(AbstractCustomContent):
"""Author profile information."""
person = models.ForeignKey(Person, on_delete=models.CASCADE)
full_name = models.CharField(max_length=200)
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to="authors/", null=True, blank=True)
email = models.EmailField(blank=True)
class CMSConfig:
enable_versioning = True
def __str__(self):
return self.full_name
# THIS IS REQUIRED for invite_m2m_relations to work
# Note: Create relation factory for the Grouper (Person), not Content model
PersonRelation = custom_relation_factory(Person)
Step 2: Configure ArticleContent for M2M¶
Update the ArticleContent model in my_content/models.py to point to Person:
class ArticleContent(AbstractCustomContent):
"""The editable article content."""
article = models.ForeignKey(Article, on_delete=models.CASCADE)
title = models.CharField(max_length=200)
slug = models.SlugField()
body = models.TextField()
class CMSConfig:
editable = True
versionable = True
apphook = True
# Request authors from the Person model
invite_m2m_relations = [("authors", "my_content.Person")]
def __str__(self):
return self.title
def __str__(self):
return self.title
Step 3: Register Person with Admin¶
Add to my_content/admin.py:
from .models import Person, PersonContent
@admin.register(Person)
class PersonAdmin(admin.ModelAdmin):
list_display = ("id",)
@admin.register(PersonContent)
class PersonContentAdmin(admin.ModelAdmin):
list_display = ("full_name", "email")
Step 5: Create Migrations¶
python manage.py makemigrations my_content
python manage.py migrate my_content
Key Concepts¶
invite_m2m_relations
The invite_m2m_relations configuration tells ArticleContent:
“I want to link to Person objects”
“Please add an
authorsaccessor to my instances”“Use a generic through model to handle the relationship”
This creates a bidirectional relationship:
# From ArticleContent side:
article.authors.all() # Get authors of this article
# There's no direct reverse accessor on Person
# (it doesn't know about articles)
Generic M2M Benefits
One Person can be linked to many ArticleContent instances
The same Person model can be linked to other content types without modification
The through model (PersonRelation) is shared across all relations
Next Steps¶
Learn more about Set Up Many-to-Many Relations and the difference between
relate_toandinvite_m2m_relationsExplore M2M Relationships Explained to understand how generic M2M relations work under the hood
Check Reference for complete API reference