Niq_Lin
Niq_Lin

Reputation: 115

Cannot get blog listing using get_context to work properly

I am trying to make a blog listing page using wagtail 2.13 but only managed to get {{ post.date }} and {{ post.url }} parts working.

{{ post.heading }} and {{ post.standfirst }} do not appear. I tried some permutations like {{ post.heading }} {{ post.content.heading }} {{ post.block.heading }} but they did not work.

What would be the correct {{ }} query to use here?

Image of partially working listing

#models.py

from django.db import models

from wagtail.core.models import Page
from wagtail.core.fields import StreamField
from wagtail.core import blocks
from wagtail.admin.edit_handlers import FieldPanel, StreamFieldPanel
from wagtail.images.blocks import ImageChooserBlock

richtext_features = [
    'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 
    "ol", "ul", "hr", 
    "link", "document-link", 
    "image", "embed",
    "code", "blockquote",
    "superscript", "subscript", "strikethrough",
]


class BlogListingPage(Page):
    """Listing page lists all the Blog pages."""

    template = "home/home_page.html"

    custom_title = models.CharField(
        max_length=255,
        blank=False,
        null=False,
        help_text='Overwrites the default title',
    )

    content_panels = Page.content_panels + [
        FieldPanel("custom_title"),
    ]

    def get_context(self, request, *args, **kwargs):
        """Adding custom stuff to our context."""
        context = super().get_context(request, *args, **kwargs)
        context["posts"] = BlogPage.objects.live().public().order_by('-date')
        return context

class BlogPage(Page):
    """ For individual blog pages."""

    author = models.CharField(max_length=255, default="Niq")
    date = models.DateField("Post date")
    content = StreamField([
        ('heading', blocks.CharBlock(form_classname="full title")),
        ('standfirst', blocks.CharBlock()),
        ('paragraph', blocks.RichTextBlock(features=richtext_features)),
        ('image', ImageChooserBlock()),
    ], block_counts={
        'heading': {'min_num': 1, 'max_num': 1,},
        'standfirst': {'max_num': 1,},
    })

    content_panels = Page.content_panels + [
        FieldPanel('author'),
        FieldPanel('date'),
        StreamFieldPanel('content'),
    ]

    template = "blog/blog_page.html"

#home_page.html

{% extends 'base.html' %}

{% block content %}

    <div class="container">
        {% for post in posts %}
            <div class="row mt-5 mb-5">
                <div class="col-sm-9">
                    <a href="{{ post.url }}">
                    test test
                        <h2>{{ post.heading }}</h2>
                        <h2>{{ post.standfirst }}</h2>
                        <h2>{{ post.date }}</h2>
                        <a href="{{ post.url }}" class="btn btn-primary mt-4">Read More</a>
                    </a>
                </div>
            </div>
        {% empty %}
        <h2>No posts</h2>
        {% endfor %}
    </div>

{% endblock content %}

Upvotes: 1

Views: 117

Answers (1)

gasman
gasman

Reputation: 25227

Items within a StreamField behave as a list - you can't access them by name, only by their position. In general, something like post.content.heading could not work, because a StreamField could have multiple heading items, or none at all. (In this case, your block_counts definition prevents that, but that doesn't change the way that elements are accessed.)

As a way around this, you could define a heading method or property on BlogPage, which loops over the StreamField value to find a block of the appropriate type:

class BlogPage(Page):
    # ...

    @property
    def heading(self):
        for block in self.content:
            if block.block_type == 'heading':
                return block.value

You will then be able to access this property from the template, using {{ post.heading }}.

Upvotes: 1

Related Questions