I’m using django as my web frontend for my podcast. I use a CDN for hosting all my media, and lately I have wanted that same CDN to host my RSS Feed, not directly django.
The current setup has some basic well known primitives:
path('podcast.xml', SubscribeFeed(), name='episode_feed'),
Some additional context; SubscribeFeed is a Syndication Feed (https://github.com/django/django/blob/main/django/contrib/syndication/views.py#L27) that is based off of the https://docs.djangoproject.com/en/4.0/ref/contrib/syndication/#custom-feed-generators
And it looks somewhat like:
class iTunesFeed(Rss201rev2Feed): content_type = 'application/xml; charset=utf-8' def root_attributes(self): attrs = super().root_attributes() attrs['xmlns:itunes'] = 'http://www.itunes.com/dtds/podcast-1.0.dtd' return attrs < ... Ripped right from the custom feed generators with a ton of extra elements for the specific podcast category and such ... > class SubscribeFeed(Feed): title = "My Cool Podcast" description = About.objects.first().about author_email = "podcast@gmail.com" author_name = "John Doe" feed_type = iTunesFeed def item_pubdate(self, item): return item.pub_date < ... More stuff that isn't relevant ... >
Just to be clear this view works perfectly with Django serving the podcast RSS. It works in all major platforms and is very nice.
My problem/question is that: I want to generate/render this view to a text file, the purpose being; I host media on a CDN, I would also benefit myself to put this RSS Feed on a separate feeds.mypodcast.com CDN. In this way, the django app could go down but the feed and media would still be accessible via CDN. The django app is only in the critical path for; episode descriptions, contact forms, web ui episode browsing, etc.
I am already running celery to release the episodes at a given interval so my plan is the following:
Create an async function run after a model save() function that
- Generates a new feed to a file or string
- Compares what’s on the CDN
- Replaces the CDN RSS feed file and invalidates CDN cache.
Step 2 and 3 are very simple but I’m struggling with generating to a file.
Things I have tried:
- “Mocking” requests and running get_feed
podcast_feed.get_feed(podcast_feed.items(), <mocked request object>)
** This certainly “works” but calling my own REST resources feels bad and is not great. - Creating a “hidden” URL dispatcher. Then on a celery beat schedule, I hit this URL from my celery task. Generate the view via requests, compare and redistribute on CDN. ** This also “works” but similarly feels bad.
- Writing my own
get_feed()
type of function. ** This didn’t really work. Reading the source I found it rather dependent on a valid request and I didn’t think I was architecturally doing it in the right place. (creating my own feedgenerator) render_to_string
but for Syndication Feeds. ** This stopped almost immediately. Worked well on a normal view but couldn’t figure it out on feeds.
I’ve been banging my head on this for quite a while. The “hidden” dispatch feels like the safest and most efficacious hack, but I’d prefer to do it the right way.
What my question is really looking for is architectural guidance. What is the right way to do this? I was hoping to find something obvious like render_to_string
but for syndication feeds. Implementing my own feedgenerator
was not working well.
What is the best path forward to solve the use case of simply; Rendering a Syndication Feed View to a text file?
Thanks.
Advertisement
Answer
I am selecting my answer because I thought a bit more about the problem set. Django’s MVT/MVC pattern is really nice, but I think I was asking it to do something it ought not do.
Making a View from a Template ala Django Syndication Feed was great if I wanted to serve the RSS feed from the Django app itself. But as stated in the question I wanted to write this RSS feed off to some remote CDN.
I accomplished this by writing my own wrapper around rfeed (note: the one in pypi is a older fork…). Which pulls in all my episodes and podcast metadata models, feeds them into items, then to a feed.
I then wrote a simple async task on model save (or scheduled episode release) that simply generates the rfeed RSS feed, creates an S3 object, diffs what’s in S3, then conditionally pushes to S3 and invalidates the cloudfront CDN.
I quite like this approach because now my django app should something bad happen go offline. All media and RSS is now in the CDN, the site is just used for show notes, contact forms, about us, simple things. The critical infra is now the cloud providers problem.