William Tekimam
William Tekimam

Reputation: 91

How to call Grandchild in Wagtail

I am using Wagtail for Django and want to call a grandchildren in the template but don't know how to do that. I am new at this and can't seem to find anything in the wagtail documentation. I see the Page Query Reference docs here but don't know how to apply it.

My Django project tree are as follows:

project/
    home/
        models.py
            > class HomePage
    news/
        models.py
            > class NewsPostPage
            > class NewsIndexPage

In the home template, I want to insert the most current news from the news app to the template.

home/models.py

from django.db import models
from django.utils.translation import ugettext_lazy as _

from modelcluster.models import ParentalKey

from wagtail.admin.edit_handlers import FieldPanel
from wagtail.core.models import Page

from news.models import NewsIndexPage

class HomePage(Page):
    why_choose_us_title_en = models.CharField(
        verbose_name=_('[EN] Why Choose Us Title'),
        max_length=200,
        default='',
        blank=True,
    )

    why_choose_us_subtitle_en = models.CharField(
        verbose_name=_('[EN]Why Choose Us Subtitle'),
        max_length=200,
        default='',
        blank=True,
    )

    content_panels = Page.content_panels + [
        FieldPanel('why_choose_us_title_en', classname='full title'),
        FieldPanel('why_choose_us_subtitle_en'),
    ]

    subpage_types = [
        'news.NewsIndexPage',
    ]

    @property
    def featured_products(self):
        return ProductPage.objects.filter(featured=True)

    news_index = NewsIndexPage

news/models.py

from datetime import timedelta

from django.db import models
from django.utils.translation import ugettext_lazy as _
from django.utils import timezone

from wagtail.admin.edit_handlers import FieldPanel, RichTextFieldPanel
from wagtail.core.fields import RichTextField
from wagtail.core.models import Page
from wagtail.images.edit_handlers import ImageChooserPanel

from base.models import TranslatedField


class NewsPostPage(Page):
    news_title_en = models.CharField(
        max_length=7250,
        verbose_name=_('[EN] News Post Page Title'),
        blank=True,
        default='',
    )
    news_text_en = RichTextField(
        verbose_name=_('[EN] News Text'),
        blank=True,
        default='',
        help_text=_('[EN] News content')
    )
    preview_text_en = RichTextField(
        verbose_name=_('[EN] News Preview Text'),
        blank=True,
        default='',
        help_text=_('[EN] Index Page News Preview Text')
    )
    created = models.DateField(
        verbose_name=_('Release Date'),
        blank=True
    )

    @property
    def recent_posts(self):
        two_months_before = timezone.now().date() - timedelta(days=60)
        posts = NewsPostPage.objects.filter(created__gt=two_months_before)
        return posts

    content_panels = Page.content_panels + [
        FieldPanel('news_title_en', classname='title full'),
        RichTextFieldPanel('news_text_en'),
        RichTextFieldPanel('preview_text_en'),
        FieldPanel('created')
    ]

    parent_page_types = ['NewsIndexPage']


class NewsIndexPage(Page):
    news_index_title_en = models.CharField(
        max_length=250,
        verbose_name=_('[EN] News Index Title'),
        blank=True,
        default='',
    )
    news_index_image = models.ForeignKey(
        'wagtailimages.Image',
        related_name='+',
        null=True,
        on_delete=models.SET_NULL
    )

    content_panels = Page.content_panels + [
        FieldPanel('news_index_title_en'),
        ImageChooserPanel('news_index_image'),
    ]

    subpage_types = ['NewsPostPage']

    @property
    def news_posts(self):
        return self.get_children().live().specific()

    @property
    def recent_posts(self):
        two_months_before = timezone.now().date() - timedelta(days=60)
        posts = NewsPostPage.objects.filter(created__gt=two_months_before)
        return posts

home/template

{% block content %}
<div id="kotak-news-events" class="kotak-grid">
     <h4 class="title-strip title-kotak-bawah">Recent News and Events</h4>
     <table>
         {% for post in page.news_index.news_posts.all %}
             <tr>
                 <td>{{ post.created }}</td>
                 <td>{{ post.translated_preview_text|richtext }}</td>
             </tr>
         {% endfor %}
    </table>
</div>

{% endblock %}

I don't know how to insert the items in NewsPostPage to make it so that it shows in the home template. Thanks in advance

Upvotes: 1

Views: 227

Answers (2)

William Tekimam
William Tekimam

Reputation: 91

It turns out you only need to add one property and I solved by adding a custom property as shown below:

class HomePage(Page):
    why_choose_us_title_en = models.CharField(
        verbose_name=_('[EN] Why Choose Us Title'),
        max_length=200,
        default='',
        blank=True,
    )

    why_choose_us_subtitle_en = models.CharField(
        verbose_name=_('[EN]Why Choose Us Subtitle'),
        max_length=200,
        default='',
        blank=True,
    )

    content_panels = Page.content_panels + [
        FieldPanel('why_choose_us_title_en', classname='full title'),
        FieldPanel('why_choose_us_subtitle_en'),
    ]

    subpage_types = [
        'news.NewsIndexPage',
    ]

    @property
    def featured_products(self):
        return ProductPage.objects.filter(featured=True)

    # removed this line
    # news_index = NewsIndexPage

    # added this code
    @property
    def news_index(self):
        return NewsPostPage.objects.live()

And still use this for the HTML

{% block content %}
<div id="kotak-news-events" class="kotak-grid">
     <h4 class="title-strip title-kotak-bawah">Recent News and Events</h4>
     <table>
         {% for post in page.news_index.all %}
             <tr>
                 <td>{{ post.created }}</td>
                 <td>{{ post.translated_preview_text|richtext }}</td>
             </tr>
         {% endfor %}
    </table>
</div>

{% endblock %}

Upvotes: 1

Kalob Taulien
Kalob Taulien

Reputation: 1918

What you want to do is add additional context to your template. Wagtail has this cool feature of mixing Views and Models together. So all you'd have to do is add your @properties to the context your sending to the template. Something like this:

class NewsIndexPage(Page):

    template = 'templates/news_index_page.html'

    @property
    def news_posts(self):
        return self.get_children().live()

    @property
    def recent_posts(self):
        two_months_before = timezone.now().date() - timedelta(days=60)
        posts = NewsPostPage.objects.filter(created__gt=two_months_before)
        return posts

    def get_context(self, request, *args, **kwargs):
        """Add posts to context"""
        context = super().get_context(request)
        context['recent_posts'] = self.recent_posts
        context['news_posts'] = self.news_posts
        return context

And because you're returning QuerySets to the template, you can loop through them normally. So your template would look something like this:

{% block content %}
<div id="kotak-news-events" class="kotak-grid">
     <h4 class="title-strip title-kotak-bawah">Recent News and Events</h4>
     <table>
         {% for post in news_posts %} {# Notice the loop here #}
             <tr>
                 <td>{{ post.created }}</td>
                 <td>{{ post.translated_preview_text|richtext }}</td>
             </tr>
         {% endfor %}
    </table>
</div>
{% endblock %}

Upvotes: 2

Related Questions