Django Spellbook integrates with Django's sitemap framework to automatically generate SEO-friendly sitemaps from your markdown content.
Quick Setup
1. Create sitemaps.py in your project:
# myproject/sitemaps.py
from django_spellbook.sitemaps import SpellbookSitemap
sitemaps = {
'spellbook': SpellbookSitemap,
}
2. Add sitemap URL to urls.py:
# myproject/urls.py
from django.contrib.sitemaps.views import sitemap
from .sitemaps import sitemaps
urlpatterns = [
# ... your other URLs
path('sitemap.xml', sitemap, {'sitemaps': sitemaps}, name='sitemap'),
]
3. Visit /sitemap.xml — your sitemap is ready.
How It Works
When you run python manage.py spellbook_md:
-
Spellbook processes all markdown files
-
Generates a
spellbook_manifest.jsonin each content app -
The manifest contains page paths, titles, and dates
When a search engine requests /sitemap.xml:
-
Django calls
SpellbookSitemap -
It reads the manifest files
-
Returns all public pages with their metadata
Sitemaps are generated dynamically at request time, so they're always up-to-date with your latest spellbook_md run.
Output Format
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>https://your-site.com/docs/intro/</loc>
<lastmod>2025-12-08</lastmod>
<changefreq>weekly</changefreq>
<priority>0.8</priority>
</url>
<url>
<loc>https://your-site.com/docs/guide/</loc>
<lastmod>2025-12-10</lastmod>
<changefreq>weekly</changefreq>
</url>
</urlset>
Per-Page Control
Control sitemap behavior with frontmatter:
---
title: Important Page
published: 2025-12-01
modified: 2025-12-12
# Sitemap options
sitemap_priority: 0.9
sitemap_changefreq: daily
---
sitemap_priority
Hint to search engines about page importance (0.0 to 1.0).
sitemap_priority: 0.9 # High priority
sitemap_priority: 0.3 # Low priority
Default: Omitted (search engines decide)
sitemap_changefreq
How often the content is likely to change.
sitemap_changefreq: daily
| Value | Use Case |
|---|---|
always |
Live data, real-time content |
hourly |
News, frequently updated |
daily |
Blog posts, active content |
weekly |
Documentation, guides |
monthly |
Reference material |
yearly |
Archives, historical |
never |
Permanent content |
Default: weekly
sitemap_exclude
Keep page public but exclude from sitemap.
sitemap_exclude: true
Useful for: landing pages you promote differently, duplicate content, utility pages.
is_public
Exclude page from sitemap AND make it private.
is_public: false
Page is still generated but hidden from sitemap.
Multi-App Support
SpellbookSitemap automatically discovers all apps in SPELLBOOK_MD_APP.
For manual control:
from django_spellbook.sitemaps import SpellbookSitemap
sitemaps = {
'docs': SpellbookSitemap(app_names=['docs_app']),
'blog': SpellbookSitemap(app_names=['blog_app']),
}
This creates separate sitemap sections, useful for large sites.
Mixing with Other Sitemaps
Combine Spellbook pages with your existing Django sitemaps:
from django.contrib.sitemaps import Sitemap
from django_spellbook.sitemaps import SpellbookSitemap
from myapp.models import Product, BlogPost
class ProductSitemap(Sitemap):
changefreq = 'weekly'
priority = 0.8
def items(self):
return Product.objects.filter(is_active=True)
def lastmod(self, obj):
return obj.updated_at
class BlogPostSitemap(Sitemap):
changefreq = 'daily'
def items(self):
return BlogPost.objects.filter(published=True)
sitemaps = {
'products': ProductSitemap,
'blog': BlogPostSitemap,
'spellbook': SpellbookSitemap, # All Spellbook pages
}
Date Handling
<lastmod> uses dates from frontmatter:
-
modified(ormodified_at,updated,updated_at) — preferred -
published(orpublished_at,date,created,created_at) — fallback
---
title: My Page
published: 2025-12-01
modified: 2025-12-12 # ← This is used for lastmod
---
If no date is found, <lastmod> is omitted.
The Manifest File
Each content app gets a spellbook_manifest.json:
{
"generated_at": "2025-12-12T14:32:00Z",
"app_name": "docs_app",
"url_prefix": "docs",
"pages": [
{
"path": "/docs/intro/",
"title": "Introduction",
"lastmod": "2025-12-12"
},
{
"path": "/docs/guide/",
"title": "User Guide",
"lastmod": "2025-12-10"
}
]
}
Automatic filtering:
-
Pages with
is_public: falseare excluded -
Pages with
sitemap_exclude: trueare excluded
Troubleshooting
Empty sitemap
Check: Did you run spellbook_md after adding content?
python manage.py spellbook_md
Check: Are all pages set to is_public: false?
Missing pages
Check: Does the page have sitemap_exclude: true?
Check: Is the manifest file generated?
ls your_app/spellbook_manifest.json
Wrong URLs
Check: Is your SPELLBOOK_MD_URL_PREFIX configured correctly?
SPELLBOOK_MD_URL_PREFIX = ['docs', 'blog'] # Matches SPELLBOOK_MD_APP order
Quick Reference
Setup
-
Create
sitemaps.pywithSpellbookSitemap -
Add
path('sitemap.xml', ...)tourls.py -
Run
spellbook_mdto generate manifests
Frontmatter Options
sitemap_priority: 0.0–1.0
sitemap_changefreq: always, hourly, daily, weekly, monthly, yearly, never
sitemap_exclude: true/false
is_public: true/false
Date Priority
-
modified(or aliases) -
published(or aliases) -
Omitted if no date