Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 51 additions & 0 deletions apps/blogs/migrations/0005_blog_feed_url_rss_xml.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""Point the Python Insider feed at https://blog.python.org/rss.xml.

Python Insider moved from Blogger to GitHub Pages. The legacy Blogger
feed path ``feeds/posts/default?alt=rss`` is still served as a
compatibility copy, but it has no file extension so GitHub Pages sends
it as ``application/octet-stream``, which makes Safari and other
browsers download it instead of opening a feed reader. The blog's
canonical feed is ``/rss.xml``, served as ``application/xml``.

``update_blog_supernav`` looks up the Feed row by matching
``settings.PYTHON_BLOG_FEED_URL`` exactly, so the stored Feed row has to
change together with the setting. The ``blogs-subscriptions`` sidebar
box links to the feed too, so rewrite both its raw and rendered content.
"""

from django.db import migrations
from django.db.models import Value
from django.db.models.functions import Replace

OLD_FEED_URL = "https://blog.python.org/feeds/posts/default?alt=rss"
NEW_FEED_URL = "https://blog.python.org/rss.xml"


def _replace_feed_url(apps, old, new):
Feed = apps.get_model("blogs", "Feed")
Feed.objects.filter(feed_url=old).update(feed_url=new)

Box = apps.get_model("boxes", "Box")
Box.objects.filter(label="blogs-subscriptions", content__contains=old).update(
content=Replace("content", Value(old), Value(new)),
_content_rendered=Replace("_content_rendered", Value(old), Value(new)),
)


def forwards(apps, schema_editor):
_replace_feed_url(apps, OLD_FEED_URL, NEW_FEED_URL)


def backwards(apps, schema_editor):
_replace_feed_url(apps, NEW_FEED_URL, OLD_FEED_URL)


class Migration(migrations.Migration):
dependencies = [
("blogs", "0004_normalize_blogentry_urls"),
("boxes", "0004_alter_box_creator_alter_box_last_modified_by"),
]

operations = [
migrations.RunPython(forwards, backwards),
]
61 changes: 61 additions & 0 deletions apps/blogs/tests/test_migrations.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Tests for blogs data migrations."""

from django.db import connection
from django.db.migrations.executor import MigrationExecutor
from django.test import TransactionTestCase

OLD_FEED_URL = "https://blog.python.org/feeds/posts/default?alt=rss"
NEW_FEED_URL = "https://blog.python.org/rss.xml"
BEFORE = [
("blogs", "0004_normalize_blogentry_urls"),
("boxes", "0004_alter_box_creator_alter_box_last_modified_by"),
]
AFTER = [("blogs", "0005_blog_feed_url_rss_xml")]


class BlogFeedUrlRssXmlMigrationTests(TransactionTestCase):
"""The 0005 migration repoints the Feed row and sidebar box at rss.xml."""

def setUp(self):
self.executor = MigrationExecutor(connection)
self.executor.migrate(BEFORE)
apps = self.executor.loader.project_state(BEFORE).apps
self.Feed = apps.get_model("blogs", "Feed")
self.Box = apps.get_model("boxes", "Box")

def tearDown(self):
self.executor.loader.build_graph()
self.executor.migrate(self.executor.loader.graph.leaf_nodes())

def _migrate(self, targets):
self.executor.loader.build_graph()
self.executor.migrate(targets)
return self.executor.loader.project_state(targets).apps

def test_forwards_rewrites_feed_and_box(self):
self.Feed.objects.create(name="Python Insider", website_url="https://blog.python.org", feed_url=OLD_FEED_URL)
self.Feed.objects.create(name="Other", website_url="https://example.org", feed_url="https://example.org/feed")
self.Box.objects.create(
label="blogs-subscriptions",
content=f'<a href="{OLD_FEED_URL}">RSS</a>',
content_markup_type="html",
)

apps = self._migrate(AFTER)

Feed = apps.get_model("blogs", "Feed")
self.assertEqual(Feed.objects.get(name="Python Insider").feed_url, NEW_FEED_URL)
self.assertEqual(Feed.objects.get(name="Other").feed_url, "https://example.org/feed")
box = apps.get_model("boxes", "Box").objects.get(label="blogs-subscriptions")
self.assertEqual(box.content.raw, f'<a href="{NEW_FEED_URL}">RSS</a>')
self.assertIn(NEW_FEED_URL, box.content.rendered)
self.assertNotIn(OLD_FEED_URL, box.content.rendered)

def test_backwards_restores_old_url(self):
self.Feed.objects.create(name="Python Insider", website_url="https://blog.python.org", feed_url=OLD_FEED_URL)
self._migrate(AFTER)

apps = self._migrate(BEFORE)

Feed = apps.get_model("blogs", "Feed")
self.assertEqual(Feed.objects.get(name="Python Insider").feed_url, OLD_FEED_URL)
4 changes: 2 additions & 2 deletions fixtures/boxes.json
Original file line number Diff line number Diff line change
Expand Up @@ -474,9 +474,9 @@
"created": "2014-02-13T18:27:24.143Z",
"updated": "2025-04-21T17:45:00.000Z",
"label": "blogs-subscriptions",
"content": "<h2 class=\"widget-title\"><span class=\"blog-name\">Python Insider</span> Subscriptions</h2>\r\n<p>Subscribe to Python Insider via:</p>\r\n<ul class=\"subscription-channels menu\">\r\n <li><a href=\"https://blog.python.org/feeds/posts/default?alt=rss\"><span aria-hidden=\"true\" class=\"icon-feed\"></span>RSS</a></li>\r\n</ul>\r\n<p>Also check out the <a href=\"https://discuss.python.org/\">Discussions on Python.org</a></p>",
"content": "<h2 class=\"widget-title\"><span class=\"blog-name\">Python Insider</span> Subscriptions</h2>\r\n<p>Subscribe to Python Insider via:</p>\r\n<ul class=\"subscription-channels menu\">\r\n <li><a href=\"https://blog.python.org/rss.xml\"><span aria-hidden=\"true\" class=\"icon-feed\"></span>RSS</a></li>\r\n</ul>\r\n<p>Also check out the <a href=\"https://discuss.python.org/\">Discussions on Python.org</a></p>",
Comment thread
JacobCoffee marked this conversation as resolved.
Resolved
"content_markup_type": "html",
"_content_rendered": "<h2 class=\"widget-title\"><span class=\"blog-name\">Python Insider</span> Subscriptions</h2>\r\n<p>Subscribe to Python Insider via:</p>\r\n<ul class=\"subscription-channels menu\">\r\n <li><a href=\"https://blog.python.org/feeds/posts/default?alt=rss\"><span aria-hidden=\"true\" class=\"icon-feed\"></span>RSS</a></li>\r\n</ul>\r\n<p>Also check out the <a href=\"https://discuss.python.org/\">Discussions on Python.org</a></p>"
"_content_rendered": "<h2 class=\"widget-title\"><span class=\"blog-name\">Python Insider</span> Subscriptions</h2>\r\n<p>Subscribe to Python Insider via:</p>\r\n<ul class=\"subscription-channels menu\">\r\n <li><a href=\"https://blog.python.org/rss.xml\"><span aria-hidden=\"true\" class=\"icon-feed\"></span>RSS</a></li>\r\n</ul>\r\n<p>Also check out the <a href=\"https://discuss.python.org/\">Discussions on Python.org</a></p>"
}
},
{
Expand Down
2 changes: 1 addition & 1 deletion pydotorg/settings/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@
HAYSTACK_CUSTOM_HIGHLIGHTER = "pydotorg.search.Highlighter"

### Blog Feed URL
PYTHON_BLOG_FEED_URL = "https://blog.python.org/feeds/posts/default?alt=rss"
PYTHON_BLOG_FEED_URL = "https://blog.python.org/rss.xml"
Comment thread
JacobCoffee marked this conversation as resolved.
Resolved
PYTHON_BLOG_URL = "https://blog.python.org"

### Registration mailing lists
Expand Down
2 changes: 1 addition & 1 deletion templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@
<link rel="alternate" type="application/rss+xml" title="Python Software Foundation News"
href="https://pyfound.blogspot.com/feeds/posts/default?alt=rss">
<link rel="alternate" type="application/rss+xml" title="Python Insider"
href="https://blog.python.org/feeds/posts/default?alt=rss">
href="https://blog.python.org/rss.xml">
<link rel="alternate" type="application/rss+xml" title="Python Releases"
href="https://www.python.org/downloads/feed.rss">

Expand Down
Loading