I apologize for any mistakes, English is not my native language.
I am making site with news and events of multiple organizations (each organization has their own account allowing them to create news and events by themself).
Using the news as an example:
NewsIndexPage
is parent for all NewsArticlePage
s.
Each NewsArticlePage
is linked with organization.
At url /news/
NewsIndexPage
shows all news and allow to filter news by organization with RoutablePageMixin
(/news/organization/<str: organization_slug>/
).
Also each organization has their own landing page (OrganizationPage
) with organization's info and news section.
News section shows up to 3 latest news of organization (like in bakerydemo repository, when latest 3 blog entries is listed in HomePage
:
Each OrganizationPage
references NewsIndexPage
through ForeignKey
.
But I don't know, how to filter children of NewsIndexPage
when it accessed through ForeignKey
.
When I access OrganizationPage
instance I want to pass organization's slug to linked NewsIndexPage
so I can filter children NewsArticlesPage
s.
The only thing I came up with is to create template_tag that will pass slug as an argument to some kind of get_news_articles(self, organization_slug)
function. But I haven't been able to do that yet.
news.models:
class NewsArticlePage(Page):
"""
A news article page.
"""
image = models.ForeignKey(
"wagtailimages.Image",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
help_text=_("Landscape mode only; horizontal width between 1000px and 3000px."),
)
body = StreamField(
BaseStreamBlock(), verbose_name=_("News article page body"), blank=True, use_json_field=True
)
date_published = models.DateField(_("Date article published"), blank=True, null=True)
organization = models.ForeignKey(
to = Organization,
on_delete = models.SET_NULL,
related_name = "news_articles",
blank=True,
null=True,
)
content_panels = Page.content_panels + [
FieldPanel("introduction"),
FieldPanel("image"),
FieldPanel("body"),
FieldPanel("date_published"),
]
private_panels = [
FieldPanel("organization"),
]
edit_handler = TabbedInterface([
ObjectList(content_panels, heading=_("Details")),
ObjectList(private_panels, heading=_("Admin only"), permission="superuser"),
])
search_fields = Page.search_fields + [
index.SearchField("body"),
]
# Specifies parent to NewsArticlePage as being NewsIndexPages
parent_page_types = ["NewsIndexPage"]
# Specifies what content types can exist as children of NewsArticlePage.
# Empty list means that no child content types are allowed.
subpage_types = []
base_form_class = NewsArticlePageForm
class Meta:
verbose_name = _("News article page")
verbose_name_plural = _("News article pages")
class NewsIndexPage(RoutablePageMixin, Page):
"""
Index page for news.
"""
introduction = models.TextField(help_text=_("Text to describe the page"), blank=True)
content_panels = Page.content_panels + [
FieldPanel("introduction"),
]
def get_context(self, request):
context = super(NewsIndexPage, self).get_context(request)
context["news_articles"] = (
NewsArticlePage.objects.live().order_by("-date_published")
)
return context
def children(self):
return self.get_children().specific().live()
@route(r"^organization/$", name="news_of_organization")
@path("organization/<str:organization_slug>/", name="news_of_organization")
def news_of_organization(self, request, organization_slug=None):
"""
View function for the organization's news page
"""
try:
organization = Organization.objects.get(slug=organization_slug)
except Organization.DoesNotExist:
if organization:
msg = "There are no news articles from organization {}".format(organization.get_shorten_name())
messages.add_message(request, messages.INFO, msg)
return redirect(self.url)
news_articles = NewsArticlePage.objects.live().filter(organization__slug=organization_slug).order_by("-date_published")
return self.render(request, context_overrides={
'title': _("News"),
'news_articles': news_articles,
})
def serve_preview(self, request, mode_name):
# Needed for previews to work
return self.serve(request)
subpage_types = ["NewsArticlePage"]
class Meta:
verbose_name = _("News index page")
verbose_name_plural = _("News index pages")
organizations. Models:
class Organization(index.Indexed, models.Model):
# ...
name = models.CharField(max_length=255)
# ...
slug = models.SlugField(
verbose_name=_("Slug Name"),
max_length=255,
unique=True,
blank=True,
null=False,
)
# ...
class OrganizationPage(Page):
"""
An organization page.
"""
organization = models.OneToOneField(
to=Organization,
on_delete=models.SET_NULL,
related_name="organization_page",
verbose_name=_("Organization"),
null=True,
blank=True,
)
# Featured sections on the HomePage
# News section
news_section_title = models.CharField(
blank=True, max_length=255, help_text=_("Title to display")
)
news_section = models.ForeignKey(
"news.NewsIndexPage",
null=True,
blank=True,
on_delete=models.SET_NULL,
related_name="+",
help_text=_(
"News section. Will display up to three latest news articles."
),
verbose_name=_("News section"),
)
content_panels = Page.content_panels + [
MultiFieldPanel(
[
FieldPanel("news_section_title"),
FieldPanel("news_section"),
],
heading=_("News section"),
),
]
private_panels = [
FieldPanel("organization"),
]
edit_handler = TabbedInterface([
ObjectList(content_panels, heading=_("Details")),
ObjectList(Page.promote_panels, heading=_("Promote")),
ObjectList(private_panels, heading=_("Admin only"), permission="superuser"),
])
# Specifies parent to OrganizationPage as being OrganizationsIndexPage
parent_page_types = ["OrganizationsIndexPage"]
# Specifies what content types can exist as children of OrganizationPage.
# subpage_types = ["news.NewsIndexPage"]
subpage_types = []
class Meta:
verbose_name = _("Organization page")
verbose_name_plural = _("Organization pages")
templates/organizations/organizations/organization_page.html:
<!-- ... -->
<div class="container">
<div class="row">
<div class="news-articles-list">
{% if page.news_section %}
<h2 class="featured-cards__title">{{ page.news_section_title }}</h2>
<div class="row">
{% for news_article in page.news_section.children|slice:"3" %}
{% include "includes/card/news-listing-card.html" with news_article=news_article %}
{% endfor %}
</div>
<a class="featured-cards__link" href="/news/organization/{{ page.slug }}">
<span>View more of our news</span>
</a>
{% endif %}
</div>
</div>
</div>
<!-- ... -->
I would be grateful for any help and tips!
Don't try to filter the query inside the template - do it in a get_context
method on the page model:
class OrganizationPage(Page):
# ...
def get_context(self, request, *args, **kwargs):
context = super().get_context(request, *args, **kwargs)
context['news_articles'] = NewsArticlePage.objects.child_of(self.news_section).filter(organization=self.organization).live()[:3]
return context
This will make a variable news_articles
available on the template, which you can loop over:
{% for news_article in news_articles %}
{% include "includes/card/news-listing-card.html" with news_article=news_article %}
{% endfor %}