Reputation: 1329
I have a site that I've structured like so:
Top Page: Animals
Subpage: Cat
Subpage: Dog
Top Page: Cat
Subpage: Food
Top Page: Dog
Subpage: Food
That way, a user can go to site/animals or site/cat. Animals is high-level info, Cat and Dog are more detailed parts of the site.
What I want to do:
Occasionally, I want to be able to reuse pages from Animals and use them in Cat or Dog. But I don't want the url to switch from one section to another.
Example: If the user is under Animal, and they click a food article, the url should be:
animals/cats/food_article
If they click on that same article under cats, the url should look like:
cats/food_article
What I've tried:
I've tried using RoutableRouteMixin
. But that works for subpages, not for pages at the same level.
I tried overwriting the get_url_parts
method for the article models. But then I got 404 errors because the pages didn't actually exist at the url I created.
Can this be achieved in Wagtail? Or is there a Django solution I can use with Wagtail?
Upvotes: 0
Views: 975
Reputation: 5176
get_children
method which is on each Page
and comes from django-treebeard
. This method is used by the routing logic to determine if a page is a child of another (when a URL is read).AnimalPage
s, then create an ArticlePage
(e.g. Food Article), then go into the dog page AND cat page and link the food article to the page.http://localhost:8000/food-article/
, http://localhost:8000/animals/cats/food-article/
and http://localhost:8000/animals/dogs/food-article/
.ArticlePage
s under their own parent page and then use routablemixin to make that page not show any sub-urls.
ArticlesPage
, this should still work as it only checks the 'last' section of the URL). I have not tested this though.from django.db import models
from modelcluster.fields import ParentalKey
from modelcluster.models import ClusterableModel
from wagtail.admin.edit_handlers import (
InlinePanel,
PageChooserPanel,
)
from wagtail.core.models import Orderable, Page
class ArticlePage(Page):
# Articles will be available to link to any other AnimalPage and content shared
content_panels = Page.content_panels
subpage_types = [] # no sub-pages allowed
class RelatedArticle(models.Model):
# http://docs.wagtail.io/en/v2.7.1/reference/pages/panels.html#inline-panels
article_page = models.ForeignKey(
'wagtailcore.Page',
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name='+',
)
panels = [
PageChooserPanel('article_page', 'base.ArticlePage')
]
class Meta:
abstract = True
class ArticlePageRelatedPages(Orderable, RelatedArticle):
page = ParentalKey(
'base.AnimalPage',
on_delete=models.CASCADE,
related_name='related_articles'
)
class AnimalsPage(Page):
# This is our root animals page
content_panels = Page.content_panels
subpage_types = ['AnimalPage']
class AnimalPage(Page):
def get_articles(self):
# returns a queryset (not a list) of all Pages that are linked articles
return Page.objects.filter(id__in=[
r.article_page.pk for r in self.related_articles.all()
])
def get_children(self):
# override the method from django-treebeard
# can be used in the template or wherever needed for 'children'
# this method is also used when attempting to find child urls
sub_pages = super().get_children()
articles = self.get_articles()
return articles | sub_pages # merges the two querysets for the 'or' operator
content_panels = Page.content_panels + [
InlinePanel('related_articles', label='Related Articles'),
]
subpage_types = [] # no sub-pages allowed
Upvotes: 1
Reputation: 2171
You could create IncludePage as below and use it to present the same content under a different url.
from django.db import models
from wagtail.core.models import Page
from wagtail.admin.edit_handlers import PageChooserPanel
from django.http import Http404
class IncludePage(Page):
page = models.ForeignKey('wagtailcore.Page',
null=True, blank=True,
on_delete=models.SET_NULL,
related_name='+')
content_panels = Page.content_panels + [
PageChooserPanel('page'),
]
def get_template(self, *args, **kwargs):
page = self.page.specific
if type(page) == IncludePage:
raise Http404("Avoid recursion")
return page.get_template(*args, **kwargs)
def get_context(self, *args, **kwargs):
page = self.page.specific
if type(page) == IncludePage:
raise Http404("Avoid recursion")
return page.get_context(*args, **kwargs)
Note however this won't work for pages with customized serve functions (e.g. pages using RoutableRouteMixin). For those you would have to duplicate the serve functionality across in the IncludePage to match. But depending on your website this might be useful.
Upvotes: 1