Roy Tang

Programmer, engineer, scientist, critic, gamer, dreamer, and kid-at-heart.

Blog Notes Photos Links Archives About

According to the documentation here: https://djangobook.com/syndication-feed-framework/

If link doesnโ€™t return the domain, the syndication framework will insert the domain of the current site, according to your SITE_ID setting

However, I’m trying to generate a feed of magnet: links. The framework doesn’t recognize this and attempts to append the SITE_ID, such that the links end up like this (on localhost):

<link>http://localhost:8000magnet:?xt=...</link>

Is there a way to bypass this?

Comments

This is a bit gnarly, but here’s a potential solution if you don’t want to give up on the Django framework:

The problem is that the method add_domain is buried deep in a huge method within syndication framework, and I don’t see a clean way to override it. Since this method is used for both the feed URL and the feed items, a monkey patch of add_domain would need to consider this.

Django source: https://github.com/django/django/blob/master/django/contrib/syndication/views.py#L178

#Steps: 1: Subclass the Feed class you’re using and do a copy-paste override of the huge method get_feed

2: Modify the line:

link = add_domain(
    current_site.domain,
    self._get_dynamic_attr('item_link', item),
    request.is_secure(),
)

To something like:

link = self._get_dynamic_attr('item_link', item)

Here’s a way to do it with monkey patching, much cleaner.

I like to create a separate folder “django_patches” for these kinds of things:

myproject/django_patches/_init_.py

from django.contrib.syndication import views
from django.contrib.syndication.views import add_domain


def add_domain_if_we_should(domain, url, secure=False):
    if url.startswith('magnet:'):
        return url
    else:
        return add_domain(domain, url, secure=False)


views.add_domain = add_domain_if_we_should

Next, add it to your INSTALLED_APPS so that you can patch the function.

settings.py

INSTALLED_APPS = [
    'django_overrides',
    ...
]

I did end up digging through the syndication source code and finding no easy way to override it and did some hacky monkey patching. (Unfortunately I did it before I saw the answers posted here, all of which I assume will work about as well as this one)

Here’s how I did it:

def item_link(self, item):
    # adding http:// means the internal get_feed won't modify it
    return "http://"+item.magnet_link

def get_feed(self, obj, request):
    # hacky way to bypass the domain handling
    feed = super().get_feed(obj, request)
    for item in feed.items:
        # strip that http:// we added above
        item['link'] = item['link'][7:]
    return feed

For future readers, this was as of Django 2.0.1. Hopefully in a future patch they allow support for protocols like magnet.