Django app for generating XML sitemaps
Project description
Swing Sitemap
Django Swing Collection
Overview
Swing Sitemap is a powerful, CMS-agnostic Django app that extends django.contrib.sitemaps with a settings-driven, drop-in URL helper and versatile sitemap classes. It eliminates the boilerplate of sitemaps.py + urls.py wiring, reducing setup to just a few import lines.
Features
| Feature | Description |
|---|---|
| StaticSitemap | Sitemap of named Django views with support for view_name, args, kwargs, and lastmod |
| ModelSitemap | Generic queryset adapter for any Django model |
| VideoSitemap | Google Video sitemap with full video metadata support |
| NewsSitemap | Google News sitemap for news articles |
| ImageSitemap | Image sitemap with gallery support |
| HreflangMixin | Multi-language support with hreflang alternate links |
| Pagination | Automatic pagination for large sitemaps (50,000 URLs limit) |
| Caching | Built-in cache support with configurable TTL |
| Compression | Gzip compression for sitemap responses |
| Signals | Auto-invalidate cache on model changes |
| Search Engine Submission | Ping Google, Bing, and custom endpoints |
| Management Commands | CLI tools for generation, validation, and submission |
Requirements
- Python 3.12+
- Django 5.0+ / 6.0+
Installation
pip install swing-sitemap
Or with Poetry:
poetry add swing-sitemap
Add to INSTALLED_APPS:
INSTALLED_APPS = [
# ...
"django.contrib.sitemaps",
"swing.sitemap",
]
Quick Start
1. Configure Settings
# settings.py
SWING_SITEMAP = {
# Static pages sitemap
"static": {
"views": ["home", "about", "contact"],
"priority": 0.6,
"changefreq": "weekly",
},
# Model-based sitemaps
"models": {
"blog": {
"model": "blog.Post",
"filters": {"is_published": True},
"order_by": ["-published_at"],
"date_field": "updated_at",
"priority": 0.8,
"changefreq": "daily",
},
"products": {
"model": "shop.Product",
"filters": {"active": True},
"priority": 0.7,
},
},
# Caching (optional)
"cache": {
"enabled": True,
"timeout": 3600, # 1 hour
"key_prefix": "sitemap",
},
# Pagination (optional)
"pagination": {
"enabled": True,
"items_per_page": 10000,
},
}
2. Wire URLs
# urls.py
from swing.sitemap.urls import sitemap_urlpatterns
urlpatterns = [
# ... your views ...
*sitemap_urlpatterns(),
]
That's it! Your sitemap is now available at /sitemap.xml.
3. Add to robots.txt (Optional)
# settings.py
TEMPLATES = [
{
# ...
"OPTIONS": {
"context_processors": [
# ...
"swing.sitemap.context_processors.sitemap_url",
],
},
},
]
# templates/robots.txt
Sitemap: {{ sitemap_url }}
Or use the template tag:
{% load swing_sitemap %}
Sitemap: {% sitemap_url %}
Sitemap Classes
StaticSitemap
For named Django views:
from swing.sitemap import StaticSitemap
sitemap = StaticSitemap(
views=["home", "about", {"view_name": "blog:archive", "kwargs": {"year": 2024}}],
priority=0.8,
changefreq="weekly",
)
ModelSitemap
For Django model querysets:
from swing.sitemap import ModelSitemap
from blog.models import Post
sitemap = ModelSitemap(
queryset=Post.objects.filter(is_published=True),
date_field="updated_at",
priority=0.7,
)
VideoSitemap
For video content (Google Video Sitemaps):
from swing.sitemap import VideoSitemap
class MyVideoSitemap(VideoSitemap):
def items(self):
return Video.objects.filter(published=True)
def video_title(self, obj):
return obj.title
def video_description(self, obj):
return obj.description
def video_thumbnail_loc(self, obj):
return obj.thumbnail.url
NewsSitemap
For news articles (Google News Sitemaps):
from swing.sitemap import NewsSitemap
class MyNewsSitemap(NewsSitemap):
publication_name = "My News Site"
publication_language = "en"
def items(self):
return Article.objects.filter(
published_at__gte=timezone.now() - timedelta(days=2)
)
HreflangMixin
For multi-language sites:
from swing.sitemap import ModelSitemap, HreflangMixin
class I18nBlogSitemap(HreflangMixin, ModelSitemap):
languages = ["en", "de", "fr"]
def alternates(self, obj):
return {
lang: f"/{lang}/blog/{obj.slug}/"
for lang in self.languages
}
Management Commands
Generate Static Sitemap
python manage.py generate_sitemap --output sitemap.xml
Validate Sitemap
# Validate from URL
python manage.py validate_sitemap https://example.com/sitemap.xml
# Validate all configured sitemaps
python manage.py validate_sitemap --all
Submit to Search Engines
python manage.py submit_sitemap https://example.com/sitemap.xml
Clear Sitemap Cache
python manage.py clear_sitemap_cache
Celery Tasks (Optional)
For async sitemap submission:
# settings.py
SWING_SITEMAP = {
"submit": {
"sitemap_url": "https://example.com/sitemap.xml",
"endpoints": {
"google": "https://www.google.com/ping?sitemap={url}",
"bing": "https://www.bing.com/ping?sitemap={url}",
},
},
}
# tasks.py
from swing.sitemap.tasks import submit_sitemap_task
# Submit immediately
submit_sitemap_task.delay()
Composing Extra Sitemaps
Swing Sitemap is CMS-agnostic. Integrate with Wagtail or other CMSes:
from swing.sitemap import default_sitemaps, sitemap_urlpatterns
from wagtail.contrib.sitemaps import Sitemap as WagtailSitemap
sitemaps = {**default_sitemaps(), "wagtail": WagtailSitemap}
urlpatterns = [
*sitemap_urlpatterns(sitemaps=sitemaps),
]
Configuration Reference
| Setting | Type | Default | Description |
|---|---|---|---|
static.views |
list |
[] |
List of view names or dicts |
static.priority |
float |
0.5 |
Default priority for static URLs |
static.changefreq |
str |
"monthly" |
Default change frequency |
models.<key>.model |
str |
Required | Model path (e.g., "app.Model") |
models.<key>.filters |
dict |
{} |
Queryset filter kwargs |
models.<key>.order_by |
list |
[] |
Ordering fields |
models.<key>.date_field |
str |
None |
Field for lastmod |
models.<key>.priority |
float |
0.5 |
URL priority (0.0-1.0) |
models.<key>.changefreq |
str |
"monthly" |
Change frequency |
cache.enabled |
bool |
False |
Enable sitemap caching |
cache.timeout |
int |
3600 |
Cache TTL in seconds |
cache.key_prefix |
str |
"swing_sitemap" |
Cache key prefix |
pagination.enabled |
bool |
False |
Enable pagination |
pagination.items_per_page |
int |
50000 |
URLs per sitemap page |
compression.enabled |
bool |
False |
Enable gzip compression |
Signals
Automatically invalidate cache when models change:
# apps.py
from django.apps import AppConfig
class BlogConfig(AppConfig):
def ready(self):
from swing.sitemap import register_sitemap_signals
from .models import Post
register_sitemap_signals(Post)
Contributing
Contributions are welcome! Please read our Contributing Guide for details.
# Clone the repository
git clone https://github.com/swing-collection/swing-sitemap.git
cd swing-sitemap
# Install dependencies
poetry install
# Run tests
poetry run pytest
# Run linters
poetry run flake8 src/
poetry run pylint src/
License
This project is licensed under the BSD-3-Clause License. See LICENSE for details.
Links
- Documentation: https://swing.dj/
- Repository: https://github.com/swing-collection/swing-sitemap
- PyPI: https://pypi.tw.martin98.com/project/swing-sitemap/
- Issues: https://github.com/swing-collection/swing-sitemap/issues
robots.txt
Use the template tag or context processor:
{% load swing_sitemap %}
Sitemap: {% sitemap_url %}
Or enable the context processor and reference {{ SITEMAP_URL }}:
TEMPLATES = [{
"OPTIONS": {
"context_processors": [
# ...
"swing_sitemap.context_processors.sitemap_url",
],
},
}]
Settings reference
| Key | Default | Notes |
|---|---|---|
static.views |
[] |
List of URL names or {view_name, args, kwargs, lastmod} dicts. |
static.priority |
0.5 |
Default <priority> for every static entry. |
static.changefreq |
"monthly" |
Default <changefreq> for every static entry. |
models.<key>.model |
required | Dotted "app.Model". |
models.<key>.filters |
{} |
**filters passed to qs.filter(). |
models.<key>.exclude |
{} |
**filters passed to qs.exclude(). |
models.<key>.order_by |
[] |
Args passed to qs.order_by(). |
models.<key>.date_field |
None |
Attribute used for <lastmod>. |
models.<key>.location_attr |
"get_absolute_url" |
Attribute or zero-arg callable returning the URL. |
models.<key>.priority |
None |
Per-sitemap <priority>. |
models.<key>.changefreq |
None |
Per-sitemap <changefreq>. |
priority / changefreq |
{} |
Free-form per-key dicts (legacy SEO_SITEMAP_* shims fold in here). |
Backward-compatibility shims
The following legacy settings still work but emit a DeprecationWarning
the first time get_config() runs:
SEO_SITEMAP_PRIORITY→SWING_SITEMAP["priority"]SEO_SITEMAP_CHANGEFREQ→SWING_SITEMAP["changefreq"]VIDEO_SITEMAP_MODEL→SWING_SITEMAP["video"]["model"]
Testing
poetry install --with dev
poetry run pytest
Colophon
Made with ❤️ by Scape Press
Contributing
Contributions are welcome! Please fork the repository and submit a pull request with your changes.
License
This project is licensed under the BSD-3-Clause license. See the LICENSE file for details.
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 swing_sitemap-0.1.2.tar.gz.
File metadata
- Download URL: swing_sitemap-0.1.2.tar.gz
- Upload date:
- Size: 55.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
aff60922187ec980dafce1f2b8f822dfcc62a28871cb639c32095e4d8265907f
|
|
| MD5 |
238c396be21301f5443c0f39c8961a69
|
|
| BLAKE2b-256 |
b878ed3b6c52cdcdffce0b31d49d60806346eb52427bd9c2b278fb48ab12500c
|
File details
Details for the file swing_sitemap-0.1.2-py3-none-any.whl.
File metadata
- Download URL: swing_sitemap-0.1.2-py3-none-any.whl
- Upload date:
- Size: 103.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0092d67cc2789907b21fb1b4575a754bd4e31b95b997cb97f5c17e29d5a0f6e1
|
|
| MD5 |
e790ea5ad76c78a9b8145edf6a5f5b6a
|
|
| BLAKE2b-256 |
d3676104cf74ff31b0b6a2d400f5689159db99159a591a57ca0596e8aaf064f6
|