Small Django blog/news app with multi-site support (django.contrib.sites).
Project description
django-site-blog
Small Django blog / news app with multi-site support
(django.contrib.sites).
Lets editors restrict an article to one or more Site objects, or
leave the assignment empty so the article appears on every site that
runs the same Django project.
Why?
Many Django projects host multiple sites — a corporate front page, a
research portal, project subsites — from a single codebase using
django.contrib.sites. Most blog/news apps either ignore that scenario
or expect a separate deployment per site.
django-site-blog keeps one article store and lets editors say, per
article, whether it should appear on every site (the default) or be
restricted to a chosen subset. One schema, one query, no middleware
gymnastics.
Features
- Per-article site assignment via M2M to
django.contrib.sites.models.Site. - "Empty M2M = visible everywhere" default — restrict only when you need to.
draft/publishedstatus viamodel_utils.StatusModel; admin-awareget_absolute_urlso drafts link back into the admin.- Split-marker excerpts via
model_utils.SplitField(marker configurable through theSPLIT_MARKERsetting). - Multi-site-aware
ArticleDetailView(slug-based, filters bySITE_ID). Article.on_siteCurrentSiteManagerfor pure "current-site only" semantics when the empty-equals-all default isn't what you want.- Polish translation included; rest of the UI is
gettext_lazy-ready. - Admin with
filter_horizontal,list_filter, slug prepopulation.
Supported versions
Django × Python
| Django | 3.10 | 3.11 | 3.12 | 3.13 | 3.14 | Status |
|---|---|---|---|---|---|---|
| 5.2 LTS | ✓ | ✓ | ✓ | ✓ | ✓ | Active LTS (extended support Apr 2028) |
| 6.0 | — | — | ✓ | ✓ | ✓ | Mainstream Aug 2026, extended Apr 2027 |
Verified against the CI matrix in
.github/workflows/tests.yml. Also
requires django-model-utils
>=4.5,<5 (for SplitField, TimeStampedModel, StatusModel).
The 5.x release of django-model-utils changed SplitField in a way that
conflicts with explicit _article_body_excerpt declarations in migrations
(produces a duplicate-column error at migrate); the upper bound is
deliberate until upstream resolves it.
Installation
uv add django-site-blog
or with pip:
pip install django-site-blog
Add the app and django.contrib.sites to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"django.contrib.sites",
"siteblog",
]
SITE_ID = 1 # required by django.contrib.sites
Include the URLs in your project's urls.py:
urlpatterns = [
# ...
path("articles/", include("siteblog.urls")),
]
Run migrations:
python manage.py migrate
How site assignment works
Each Article has a sites M2M to django.contrib.sites.models.Site.
- Empty M2M → article visible on every site (the default).
- One or more sites set → article only visible on the listed sites.
The included ArticleDetailView enforces this with a single OR query:
Article.objects.filter(
Q(sites__isnull=True) | Q(sites__id=settings.SITE_ID)
).distinct()
If you want pure "current-site only" semantics (drop the empty-equals-all
behaviour), use the Article.on_site CurrentSiteManager instead.
Settings
| Setting | Default | Purpose |
|---|---|---|
SITE_ID |
— | Required by django.contrib.sites. Drives the per-site filtering. |
SPLIT_MARKER |
"<!-- split -->" |
Marker shown in the admin help text for Article.article_body to indicate the split-point between excerpt and full article. The actual splitting is performed by model_utils.fields.SplitField per its own settings. |
Templates
The package ships two minimal templates:
siteblog/article_detail.html— uses{% extends "siteblog/base.html" %}.siteblog/base.html— a bare HTML skeleton; override it in your project by placing asiteblog/base.htmlahead of the package's in yourTEMPLATESDIRS.
Development
git clone https://github.com/iplweb/django-site-blog.git
cd django-site-blog
uv sync --all-extras
DJANGO_SETTINGS_MODULE=tests.settings uv run pytest
pre-commit install to wire ruff + pyupgrade + django-upgrade.
License
MIT — see LICENSE.
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_site_blog-0.1.0.tar.gz.
File metadata
- Download URL: django_site_blog-0.1.0.tar.gz
- Upload date:
- Size: 10.5 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e8d7b045fd2515eb0b5cd59b3be41ff368550d1e105ee045b7929deb9831d5ce
|
|
| MD5 |
2d112993688b7ad5e1782e5235234d4d
|
|
| BLAKE2b-256 |
f2f75ac21529657e22f3bed377b56db4ffbf46c695213fc86092f333d79b9531
|
File details
Details for the file django_site_blog-0.1.0-py3-none-any.whl.
File metadata
- Download URL: django_site_blog-0.1.0-py3-none-any.whl
- Upload date:
- Size: 11.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.14 {"installer":{"name":"uv","version":"0.11.14","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0ff59ebbf4aa55343b07bfc85f7de642bab9f1858f39d4ef4aaeb4e05f25fccc
|
|
| MD5 |
68b0909ec8f9be5e971b33c55479da56
|
|
| BLAKE2b-256 |
0b52582993f92f77e7745ab7ad1c4781b9fa7ab892ef10680193802d5c2646bd
|