An RSS feed reading library for Django.
Project description
Django Feed Reader
django-feed-reader is a reusable Django app for subscribing to, fetching, and storing RSS, Atom, and JSON Feed sources.
It is designed as a backend library, not a complete reader application. It gives you Django models, polling utilities, admin integration, and read/unread helpers so you can build your own UI, APIs, and workflows on top.
What it provides
- RSS, Atom, and JSON Feed parsing
- Feed storage in Django models
- Automatic polling intervals based on feed activity
- Feed entry and enclosure persistence
- Single-user and multi-user read/unread tracking
- Optional raw JSON storage for uncommon feed attributes
- Optional Cloudflare workarounds via Dripfeed or a worker URL
- Django admin registrations for the core models
Requirements
- Python 3
- Django 3.2+
Installation
Install the package:
pip install django-feed-reader
Add feeds to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"feeds",
]
Run migrations:
python manage.py migrate
Set at least the polite-identification settings in your Django settings:
FEEDS_USER_AGENT = "ExampleReader/1.0"
FEEDS_SERVER = "https://example.com"
FEEDS_USER_AGENT is sent on outbound feed requests. FEEDS_SERVER is included in that user agent string so feed owners can identify your service.
Quick start
Create a feed source:
from feeds.models import Source
source = Source.objects.create(
feed_url="https://example.com/feed.xml",
)
Fetch it immediately:
from feeds.utils import read_feed
read_feed(source)
source.refresh_from_db()
Inspect the results:
source.name
source.description
source.posts.count()
source.posts.order_by("-created")[:10]
Core models
Source
Represents a single feed subscription.
Useful fields include:
feed_url: the URL fetched by the pollersite_url: the feed's corresponding website, when availablename: feed titledescription: feed description / summaryimage_url: feed icon or imagelast_result: human-readable result of the last fetchstatus_code: last HTTP status code seeninterval: next polling interval in minuteslive: whether the source should still be actively polled
Useful helpers include:
unread_countget_unread_posts()get_paginated_posts()mark_read()
Post
Represents one item / entry from a feed.
Important fields include:
titlebodylinkauthorcreatedguidimage_url
Enclosure
Represents media associated with a post, such as podcast audio or image attachments.
Useful helpers include:
is_imageis_audiois_video
Subscription
Represents a user following a Source.
Use Subscription when you need per-user read/unread tracking or folder-like grouping of sources.
Public utility functions
The main public API lives in feeds.utils.
read_feed(source)
Fetches one Source, parses it, and persists any changes.
Use this when:
- you want to fetch a feed immediately after creating it
- you are debugging a specific feed
- you need one-off refresh behavior
update_feeds(max_feeds=3)
Polls all due Source rows, ordered by due_poll, up to max_feeds.
Use this from cron, Celery, or another scheduled task runner.
test_feed(source, cache=False)
Performs a simple reachability test for a specific feed URL without going through the full persistence flow.
Subscription helpers
get_subscription_list_for_user(user)get_unread_subscription_list_for_user(user)
These help build folder trees and unread views for multi-user applications.
Polling behavior
The library automatically adjusts polling frequency based on whether a feed changes.
- Fastest poll frequency: 1 hour
- Slowest poll frequency: 24 hours
Feeds that change frequently are polled more often. Feeds that remain unchanged are polled less often.
The typical pattern is to run the poller every 5 to 10 minutes and let the library decide which sources are actually due.
Using the management command
The app includes:
python manage.py refreshfeeds
That command calls update_feeds(30).
Using Celery
from celery import shared_task
from feeds.utils import update_feeds
@shared_task
def refresh_feed_batch():
update_feeds(30)
Read/unread tracking
There are two supported patterns.
Single-user installations
If your project is effectively for one user, you can use the helper methods directly on Source.
source.unread_count
source.get_unread_posts()
source.mark_read()
Multi-user installations
If multiple users can follow the same feed, create Subscription rows.
from django.contrib.auth import get_user_model
from feeds.models import Source, Subscription
User = get_user_model()
user = User.objects.get(username="alice")
source = Source.objects.get(feed_url="https://example.com/feed.xml")
subscription = Subscription.objects.create(
user=user,
source=source,
name=source.display_name,
)
You can also create folder-like subscriptions by setting source=None and using parent relationships.
Settings
Required or strongly recommended
FEEDS_USER_AGENT- Example:
"ExampleReader/1.0"
- Example:
FEEDS_SERVER- Example:
"https://example.com"
- Example:
If FEEDS_SERVER is not set, the library will derive a default from ALLOWED_HOSTS where possible.
Optional
-
FEEDS_VERIFY_HTTPS(default:True)- Set to
Falseonly if you deliberately want to allow invalid HTTPS certificates.
- Set to
-
FEEDS_KEEP_OLD_ENCLOSURES(default:False)- If a feed changes enclosure URLs over time, keep the old ones and mark them with
is_current=False.
- If a feed changes enclosure URLs over time, keep the old ones and mark them with
-
FEEDS_SAVE_JSON(default:False)- Store raw feed/parser data in the
jsonfields onSourceandPost. - Useful when you want access to custom or uncommon feed attributes.
- Increases database usage.
- Store raw feed/parser data in the
-
FEEDS_DRIPFEED_KEY(default: unset)- If present, Cloudflare-blocked feeds can be retried via Dripfeed.
-
FEEDS_CLOUDFLARE_WORKER(default: unset)- Optional alternate fetch endpoint used for Cloudflare-blocked feeds.
Cloudflare support
When a feed responds in a way that looks like Cloudflare protection, the library can:
- mark the source as Cloudflare-protected
- retry through Dripfeed if
FEEDS_DRIPFEED_KEYis configured - use
FEEDS_CLOUDFLARE_WORKERif configured
What this library does not do
- It does not ship a reader UI
- It does not define URLs or views for your application
- It does not download enclosure files for you
- It does not provide a complete end-user feed reader product
Development
From a checkout:
pip install -e ".[test]"
pytest
Notes for integrators
refreshfeedsis intentionally small and fixed toupdate_feeds(30). If you need different batching behavior, callupdate_feeds()from your own scheduler.- Redirect targets are validated before being followed or persisted.
- The app stores feed metadata and parsed content, but you remain responsible for presentation and any downstream content policies in your own application.
Documentation
Full documentation is available at django-feed-reader.readthedocs.io.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_feed_reader-2.0.1b4.tar.gz.
File metadata
- Download URL: django_feed_reader-2.0.1b4.tar.gz
- Upload date:
- Size: 33.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0871ec0e9889554d8e885bbd33487bcb956d2df63f1d8e797efeaeef5948ab05
|
|
| MD5 |
c880ebaa5cc28f357539bdcefeb8b1e5
|
|
| BLAKE2b-256 |
650a350099c985bab210efa5575fb4192a78f421825befcd883888c0720592fc
|
File details
Details for the file django_feed_reader-2.0.1b4-py3-none-any.whl.
File metadata
- Download URL: django_feed_reader-2.0.1b4-py3-none-any.whl
- Upload date:
- Size: 46.0 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.1.0 CPython/3.8.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
777f30163ea018028116a6e6e260a552843cd8fcc0a5fc6ebb6795eb535a4a79
|
|
| MD5 |
91b2c18cbaa725b13ce6ae3d0915951c
|
|
| BLAKE2b-256 |
8d7e755c1d4cd0ba19d1fcdc5eae5c6fe5f23ca64a5bb8c4eabfe8c5d8e3fe74
|