0

I haven't seen any question like this on the site. So hopefully this question will be useful for others in the future.

I have a PostPage, EditorialPage, and DocumentPage. Within the models of the three, I added a 'featured' Boolean, so that if toggled in the settings, either Post is featured onto the HomePage.

This was working when it's just featured_post = PostPage.objects.live().filter(featured=True)

However, in the HomePage/Models, I only was able to get it to work with PostPage. I'm not too keen with query/querysets, but I feel it has something to do with that. I've read the Docs multiple times, but I do not understand how to make this work.

Blog/Models.py

class PostPage(Page):
    header_image = models.ForeignKey(
    "wagtailimages.Image", 
    null=True,
    blank=True, 
    on_delete=models.SET_NULL, 
    related_name="+",
)

    featured = models.BooleanField(default=False)

    highlight = models.BooleanField(default=False)

    description = models.CharField(max_length=255, blank=True,)

    body = StreamField(BodyBlock(), blank=True)

    tags = ClusterTaggableManager(through="blog.PostPageTag", blank=True)

    post_date = models.DateTimeField(
        verbose_name="Post date", default=datetime.datetime.today
    )

content_panels = Page.content_panels + [
    ImageChooserPanel("header_image"),
    FieldPanel("description", classname="full"),
    InlinePanel("blog_authors", label="author", min_num=1, max_num=4),
    MultiFieldPanel([
        InlinePanel("categories", label="category",),
        FieldPanel("tags"),
    ], heading="Meta"),
    StreamFieldPanel("body"),
]

settings_panels = Page.settings_panels + [
    FieldPanel("post_date"),
    MultiFieldPanel([
        FieldPanel("featured"),
        FieldPanel("highlight"),
    ], heading="Showcase")
]

Home/Models.py (Method 1)

class HomePage(RoutablePageMixin, Page):
    def get_context(self, request):

    featured_post = PostPage.objects.live().public().filter(featured=True)
    featured_editorial = EditorialPage.objects.live().public().filter(featured=True)
    feature_document = DocumentPage.objects.live().public().filter(featured=True)

    featured_select = sorted(
        chain(featured_post, featured_editorial, feature_document),
        key=lambda page: page.first_published_at, reverse=True)[0]
    
    return featured_select

Home/Models.py (Method 2)

class HomePage(RoutablePageMixin, Page):
def get_context(self, request):
    context = super().get_context(request)

    featured_post = PostPage.objects.live().filter(featured=True)
    featured_post = EditorialPage.objects.live().filter(featured=True)
    featured_post = DocumentPage.objects.live().filter(featured=True)
    context['featured_post'] = featured_post.live().public()
    
    return context

def get_posts(self):
    return PostPage.objects.descendant_of(self).live().order_by("-post_date")

Thank you in advance to anyone that reaches out to this post. I just started using Python in November. So I'm still fresh, I think.

Sterl
  • 17
  • 1
  • 6

1 Answers1

1

I can see that your code in 'method 1' is based on this answer How can I combine two or more querysets in a Django view?

It looks like you have added [0] to the end of your featured_select generation, this will get the first item from the list. On top of that in method 1 you are not returning the context but just returning the featured_select which will be a single Page and likely break things.

In Method 2, you are correctly adding something to context and returning it, but in this case the featured_post is only the QuerySet of post pages.

You probably need a mix of the two, something like this

def get_context(self, request):
    context = super().get_context(request)

    featured_post_pages = PostPage.objects.live().filter(featured=True)
    featured_editoral_pages = EditorialPage.objects.live().filter(featured=True)
    featured_document_pages = DocumentPage.objects.live().filter(featured=True)

    featured_pages = sorted(
        chain(featured_post_pages, featured_editoral_pages, featured_document_pages),
        key=lambda page: page.first_published_at, reverse=True)

    context['featured_pages'] = featured_pages
    
    return context

More context

When you perform queries in Django on objects, the returned data structure is a QuerySet, it is kind of like a list but has some special features and behaviours that are good to understand when working with Django. https://docs.djangoproject.com/en/4.0/ref/models/querysets/

Additional to this, when you query a class that extends Wagtail's Page, it is a Page QuerySet, which also has some subtle differences and functions available (e.g. live()).

Have a read of both of those documentation pages, but what you want is a way to 'merge' these different QuerySets into one large list.

LB Ben Johnston
  • 4,751
  • 13
  • 29