0
votes

I am trying to get my homepage in Wagtail to show a random image. I followed the Wagtail start guide and tried to incorporate this hack in the template, but I'm getting an error.

home/models.py
from django.db import models

from modelcluster.fields import ParentalKey

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

class HomePage(Page):
    body = RichTextField(blank=True)

    content_panels = Page.content_panels + [
        FieldPanel('body', classname="full"),
        InlinePanel('home_images', label='HomePage Images'),
    ]

class HomeImages(Orderable):
    page = ParentalKey(HomePage, on_delete=models.CASCADE, related_name='home_images')
    image = models.ForeignKey(
        'wagtailimages.Image', on_delete=models.CASCADE, related_name='+'
    )
    caption = models.CharField(blank=True, max_length=64)

    panels = [
        ImageChooserPanel('image'),
        FieldPanel('caption'),
    ]
home/templates/home/home_page.html
{% extends "base.html" %}
{% load static %}
{% load wagtailcore_tags wagtailimages_tags %}

{% block body_class %}template-homepage{% endblock %}

{% block content %}
{{ page.body|richtext }}
{% with page.thiswillfail|random as item %}
<div>
{% image item.image max-1000x1000 %}
<p>{{ item.caption }}</p>
</div>
{% endwith %}
{% endblock content %}

Even after adding some images in the Wagtail admin interface, I get the same error on the homepage: object of type 'DeferringRelatedManager' has no len()

I at least narrowed it down to the "with" statement in home_page.html. The wagtail image object is iterable in a for loop as in the start guide, but it would seem that it doesn't work with the random function. So, how can I get it to pick a random image object to show on the page?

1

1 Answers

3
votes

Rather than getting a random image in the template, you could get the random image at the context for the template. This avoids the needs to apply the hack mentioned, and should make the code easier to follow for future.

Each Wagtail Page can provide a few method overrides to customise how the template renders. The simplest for this case would be get_cotext - see https://docs.wagtail.io/en/stable/topics/pages.html#customising-template-context

Django allows for querysets to be randomly ordered with order_by('?'), see https://docs.djangoproject.com/en/3.0/ref/models/querysets/#order-by

Code Example

home/models.py
# models.py
class HomePage(Page):
    # ... fields & etc

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

        # Add extra variables and return the updated context
        context['random_image'] = self.home_images.order_by('?').first()
        return context
home/templates/home/home_page.html
{% extends "base.html" %}
{% load static %}
{% load wagtailcore_tags wagtailimages_tags %}

{% block body_class %}template-homepage{% endblock %}

{% block content %}
{{ page.body|richtext }}

{% image random_image.image max-1000x1000 %}

<p>{{ random_image.caption }}</p>

{% endblock content %}