Reputation: 1
How can I make a universal symmetrical relation for all models that inherit from wagtailcore.Page
(like in "Approach #1" below, but that will actually work?)
It is quite easy to make a both side relation between more than two models by creating relation classess to cover all of them. For example:
from django.db import models
from modelcluster.fields import ParentalKey
class NewsToCalendarRelation(models.Model):
news = ParentalKey('pages.NewsPage', on_delete=models.CASCADE, related_name='news_calendar_source',null=True)
calendar = ParentalKey('pages.CalendarPage', on_delete=models.CASCADE, related_name='news_calendar_target',null=True)
class Meta:
unique_together = ('news', 'calendar')
class NewsToStaticRelation(models.Model):
news = ParentalKey('pages.NewsPage', on_delete=models.CASCADE, related_name='news_static_source',null=True)
static = ParentalKey('pages.StaticPage', on_delete=models.CASCADE, related_name='news_static_target',null=True)
class Meta:
unique_together = ('news', 'static')
class StaticToCalendarRelation(models.Model):
static = ParentalKey('pages.StaticPage', on_delete=models.CASCADE, related_name='static_calendar_source',null=True)
calendar = ParentalKey('pages.CalendarPage', on_delete=models.CASCADE, related_name='static_calendar_target',null=True)
class Meta:
unique_together = ('static', 'calendar')
And then to use in page model:
class NewsPage(Page):
related_news = ParentalManyToManyField('self', blank=True,symmetrical=True)
related_calendar = ParentalManyToManyField('pages.CalendarPage', through=NewsToCalendarRelation, blank=True)
related_static = ParentalManyToManyField('pages.StaticPage', through=NewsToStaticRelation, blank=True)
content_panels = [
AutocompletePanel('related_news', target_model='pages.NewsPage'),
AutocompletePanel('related_calendar', target_model='pages.CalendarPage'),
AutocompletePanel('related_static', target_model='pages.StaticPage')
]
class CalendarPage(Page):
related_news = ParentalManyToManyField('pages.NewsPage', blank=True, through=NewsToCalendarRelation)
related_calendar = ParentalManyToManyField('self', symmetrical=True)
related_static = ParentalManyToManyField('pages.StaticPage', blank=True, through=StaticToCalendarRelation)
content_panels = [
AutocompletePanel('related_news', target_model='pages.NewsPage'),
AutocompletePanel('related_calendar', target_model='pages.CalendarPage'),
AutocompletePanel('related_static', target_model='pages.StaticPage')
]
and so for other models…
But it would be more efficient to have only one related pages field** to choose from all pages. It is no problem to define chooser field for that either PageChooserPanel
or AutocompletePanel('related_all', target_model='wagtailcore.Page')
. But there is a problem with creating an universal relation between page models with two columns. It is quite intuitive to try something like this:
class PageRelation(models.Model):
page_from = models.ForeignKey(Page, on_delete=models.CASCADE, related_name='page_relations_from',blank=True,null=True)
page_to = models.ForeignKey(Page, on_delete=models.CASCADE, related_name='page_relations_to',blank=True,null=True)
class Meta:
unique_together = ('page_from', 'page_to')
and then:
related_pages = models.ManyToManyField("wagtailcore.Page", through=PageRelation)
But this won't work because the foreign key must be explicitly set to specific page model where this field is used in and wagtailcore.Page is rejected: pages.PageRelation: (fields.E336) The model is used as an intermediate model by 'pages.NewsPage.related_pages', but it does not have a foreign key to 'NewsPage' or 'Page'.
Another approach I've tried that works but obviously lacks of symmetry is this:
class PageRelation(models.Model):
news = ParentalKey('pages.NewsPage', on_delete=models.CASCADE, related_name='news',null=True)
calendar = ParentalKey('pages.CalendarPage', on_delete=models.CASCADE, related_name='calendar',null=True)
static = ParentalKey('pages.CalendarPage', on_delete=models.CASCADE, related_name='static',null=True)
target = ParentalKey('wagtailcore.Page', on_delete=models.CASCADE, related_name='page',null=True)
class Meta:
unique_together = ('news', 'calendar','static', 'target')
And can be used as follows:
class NewsPage(Page):
related_pages = ParentalManyToManyField('wagtailcore.Page', blank=True, through=PageRelation,related_name="news_relation",through_fields=("news", "target"))
...
class CalendarPage(Page):
related_pages = ParentalManyToManyField('wagtailcore.Page', blank=True, through=PageRelation,related_name="news_relation",through_fields=("calendar", "target"))
...
The lack of symmetry is because every model has different source column.
Another approach I've found would be to use GenericForeignKey
and GenericRelation
but GenericRelation
is read-only field and cannot be used on model page PageChooserPanel
or AutocompletePanel
.
To repeat the question, how can I make an universal symmetrical relation for all models that inherit from wagtailcore.Page
?
Upvotes: 0
Views: 36