Skip to content

Improve the blog feed#426

Open
zerolab wants to merge 2 commits intomainfrom
chore/improve-blog-feed
Open

Improve the blog feed#426
zerolab wants to merge 2 commits intomainfrom
chore/improve-blog-feed

Conversation

@zerolab
Copy link
Copy Markdown
Member

@zerolab zerolab commented Mar 23, 2026

Description of Changes Made

This PR makes the following changes to the feed:

  • adds fall back to the search description field if the listing summary is not set. The search description field is added more often than the listing summary. This should make the experience nicer for users of feed readers.
  • defers StreamFields
  • adds author names
  • fetch related feed image in blog post queryset

How to Test

Go to /blog/feed, download the resulting file and open with an editor, check the outputs match

Screenshots

Expand to see more

Metadata

Checklists

MR Checklist

  • Add a description of your pull request and instructions for the reviewer to verify your work.
  • If your pull request is for a specific ticket, link to it in the description.
  • Stay on point and keep it small so the merge request can be easily reviewed.
  • Tests and linting passes.

Unit tests

  • Added
  • Not required

Documentation

Browser testing

  • I have tested in the following browsers and environments (edit the list as required)
    • Latest version of Chrome on mac
    • Latest version of Firefox on mac
    • Latest version of Safari on mac
    • Safari on last two versions of iOS
    • Chrome on last two versions of Android
  • Not required

Data protection

  • Not relevant
  • This adds new sources of PII and documents it and modifies Birdbath processors accordingly

Light and dark mode

  • I have tested the changes in both light and dark mode
  • The change is not relevant to dark and light mode

Accessibility

  • Automated WCAG 2.1 tests pass
  • HTML validation passes
  • Manual WCAG 2.1 tests completed
  • I have tested in a screen reader
  • I have tested in high-contrast mode
  • Any animations removed for prefers-reduced-motion
  • Not required

Sustainability

  • Images are optimised and lazy-loading used where appropriate
  • SVGs have been optimised
  • Performance and transfer of data considered
  • If JavaScript is needed alternatives have been considered
  • Not required

Pattern library

  • The pattern library component for this template displays correctly, and does not break parent templates
  • The styleguide is updated if relevant
  • Changes are not relevant the pattern library

- defer StreamFields
- fall back to the search description field if the listing summary is
  not set
- add author names
- fetch related feed image in blog post queryset
Copy link
Copy Markdown
Member

@helenb helenb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks @zerolab - I have reviewed and tested. Added a couple of comments and queries (with help from Claude) for you to check. Apologies for the code suggestions not replacing the right lines of code but hopefully you get the idea....


class BlogFeed(Feed):
title = "The Torchbox Blog"
link = "/blog/"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you update this while you are making changes? The link generated is https://torchbox.com/blog/ which immediately redirects to https://torchbox.com/news - we might as well directly link to /news

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I don't know if there's an option in the RSS spec to have multiple links - if there is that would be ideal as the feed combines our multiple blog pages)

def item_link(self, item):
return item.get_full_url()
def item_link(self, item: BlogPage) -> str:
return item.get_full_url(request=self.request)
Copy link
Copy Markdown
Member

@helenb helenb Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you explain this change a bit more? I get the exact same result for the urls before and after the change.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def item_enclosure_mime_type(self, item):
def item_enclosure_mime_type(self, item: BlogPage) -> str | None:
if item.feed_image:
image_format = filetype.guess_extension(item.feed_image.file)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Although this isn't a change, claude flagged a possible issue here that we could fix. filetype.guess_extension() can return None if the format can't be determined which would result in a mime type of image/None as the MIME type.

Suggested change
image_format = filetype.guess_extension(item.feed_image.file)
def item_enclosure_mime_type(self, item: BlogPage) -> str | None:
if item.feed_image:
try:
image_format = filetype.guess_extension(item.feed_image.file)
if image_format:
return f"image/{image_format}"
except (OSError, AttributeError):
pass
return None

The above was it's suggested change as 'RSS feed readers might reject enclosures with invalid MIME types like "image/None"'. I don't think it is a big issue as I don't think we'd ever have images with an invalid mime type, but could be defensive programming to update it.

return f"image/{image_format}"

def item_enclosure_length(self, item):
def item_enclosure_length(self, item: BlogPage) -> str | None:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another code review issue that claude raised - will leave you to judge if it is valid (I don't know if it's correct about what file.size returns but seems plausible that it is an int):

Issue: The return type is str | None, but file.size returns an int. This creates a type mismatch.

Suggested change
def item_enclosure_length(self, item: BlogPage) -> str | None:
def item_enclosure_length(self, item: BlogPage) -> str | None:
if item.feed_image:
return str(item.feed_image.file.size)
return None

Impact:

  • RSS spec requires enclosure length to be a string
  • Current code might work due to Python's implicit conversion, but explicit is better

@zerolab zerolab requested a review from helenb April 2, 2026 13:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants