Architecture Overview¶
This document describes the high-level architecture of Django Cast.
Django Cast is a blogging and podcasting application built on Django and Wagtail CMS.
High-Level Design¶
Django Cast follows a layered architecture:
┌─────────────────────────────────────────────────────┐
│ Frontend Layer │
│ (Templates, JavaScript, HTMX) │
└─────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────┐
│ API Layer │
│ (REST Framework + Wagtail API v2) │
└─────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────┐
│ Models Layer │
│ (Django ORM - Wagtail Pages + Models) │
└─────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────┐
│ Repository Layer │
│ (PostQuerySnapshot, PostDetailContext, etc.) │
└─────────────────────────────────────────────────────┘
│
┌─────────────────────────────────────────────────────┐
│ Database Layer │
│ (PostgreSQL/SQLite) │
└─────────────────────────────────────────────────────┘
Page Hierarchy¶
Cast uses Wagtail’s page tree to organize content:
HomePage (cast.HomePage)
├── Blog (cast.Blog) - Index page for posts
│ ├── Post (cast.Post) - Individual blog posts
│ └── Post (cast.Post)
└── Podcast (cast.Podcast) - Blog with podcast metadata (iTunes fields, etc.)
├── Episode (cast.Episode) - Post with audio enclosure
└── Episode (cast.Episode)
Podcast: A Blog subclass with extensive podcast metadata (iTunes categories, explicit content flags, etc.)
Episode: A Post subclass that includes audio files which become enclosure elements in RSS feeds
Repository Pattern¶
The repository pattern sits between the database and Django models. It provides an abstraction layer that returns dictionaries (not model instances) that are JSON-serializable. This enables caching between the database queries and model instantiation.
Flow:
Database → Django ORM/Raw SQL → Repository → Dict → JSON Cache → Dict → Django Models → Template
Key Repository Classes¶
- PostQuerySnapshot
Base class that converts querysets to dictionaries with prefetch optimization.
- PostDetailContext
Fetches single post data with all relations in minimal queries.
- FeedContext
Optimized for RSS/podcast feed generation.
- BlogIndexContext
Handles blog index pages with pagination.
The repositories:
Execute optimized queries (ORM or raw SQL)
Return dictionary structures
Enable JSON caching of query results
Allow dynamic model reconstruction from cached data
Models Architecture¶
Cast models are organized into:
Page Models (
models/pages.py):HomePage: Site rootBlog: Container for postsPost: Blog entries with StreamField contentPodcast: Blog subclass with iTunes metadata, categories, etc.Episode: Post subclass with audio fields for RSS enclosures
Media Models:
Wagtail
Image: Images with collection/uploader ownership semanticsAudio: Audio files with duration and metadataVideo: Video files with poster framesGallery: Collection of imagesChapterMark: Timestamps for podcast chaptersTranscript: Text for audio/video
Moderation (
models/moderation.py):SpamFilter: Naive Bayes spam detection
StreamField Structure¶
Posts use a two-section StreamField:
body = StreamField([
("overview", blocks.StreamBlock([...])),
("detail", blocks.StreamBlock([...])),
])
This allows showing just the overview on index pages and full content on detail pages.
Available block types include:
Text blocks (heading, paragraph, code)
Media blocks (image, gallery, video, audio)
Embed blocks (HTML, external embeds)
Performance Optimization¶
Query Optimization¶
The repository pattern minimizes database queries through:
select_related: For one-to-many foreign keys
prefetch_related: For many-to-many relations
Bulk operations: For rendition creation
Caching¶
Repository-ready data: repositories can produce JSON-serializable dicts (useful for caching, if configured)
Rendition cache: Generated image sizes
Feed cache: Feed endpoints are wrapped with Django’s
cache_page(seesrc/cast/urls.py)
Media Handling¶
Media Pipeline:
Upload: Via Wagtail admin or API
Storage: Django storage backend (local or S3)
Processing:
Images: Wagtail renditions
Audio: Duration extraction
Video: Poster generation
Delivery: Direct or via CDN
API Architecture¶
Cast provides:
Wagtail API v2: Page content access
Custom REST endpoints: Media uploads, search
Feed endpoints: RSS/podcast XML
Note: The REST Framework serializers are not performance-optimized. Performance optimization happens at the repository and feed generation level.
Search and Facet Flow¶
Search/filter/facet behavior is implemented through two parallel paths:
1. Blog list and legacy facet counts¶
Source queryset:
Blog.unfiltered_published_posts(live descendants, ordered by-visible_date)Filter layer:
Blog.get_filterset(...)createsPostFiltersetLegacy facet API:
FacetCountSerializer.get_facet_countsreads facet counts from the filterset and returnsfacet_counts.date_facets/category_facets/tag_facets
2. Modal facet counts API¶
Endpoint:
FacetCountsDetailViewwith?mode=modalSource queryset:
blog.unfiltered_published_postsCalculator:
cast.modal_facet_counts.get_modal_facet_counts
Modal calculation steps:
Normalize selection (
search,date_facets,tag_facets,category_facets)Build
result_countfrom fully selected querysetFor each configured group, recalculate
all_countwith that group excludedReturn
optionsfrom the full facet universe, including zero-count values
This split keeps legacy clients stable while enabling richer modal UIs that need “what would happen if I click this facet next?” counts.
Frontend Components¶
Templates: Django/Wagtail templates
JavaScript:
Build system: Vite
Podcast player: Podlove (Vue.js app wrapped in web component)
Web components: Gallery viewer (used by GalleryWithLayout blocks)
Interactivity: HTMX
Styling: CSS (fully customizable via themes)
Code Organization¶
src/cast/
├── api/ # REST API
├── blocks.py # StreamField blocks
├── feeds.py # RSS/podcast feeds
├── management/ # Django commands
├── migrations/ # Database migrations
├── models/ # Model definitions
├── static/ # Built assets
├── templates/ # Django templates
├── views/ # Django views
└── wagtail_hooks.py # Admin customizations
Extension Points¶
StreamField Blocks: Add new content types in
blocks.pyThemes: Override templates or install theme packages (e.g., cast-vue, cast-bootstrap5)
Management Commands: Add CLI tools
API Endpoints: Extend REST API
Theme System¶
Themes provide complete control over the frontend:
Template replacement: Override any/all templates
Full customization: Use any CSS framework or approach
SPA support: Can function as headless CMS (e.g., cast-vue uses Vue.js SPA)
Package distribution: Themes as PyPI packages
The theme system allows everything from minor template tweaks to complete frontend replacements.
Deployment¶
Typical deployment:
Application: Django app server
Static files: Collected and served via your preferred static pipeline (e.g. nginx/CDN; whitenoise optional)
Media files: CDN or Django direct serving
Database: PostgreSQL (recommended) or SQLite
Caching: Django cache backend (for
cache_pageand any optional repository caching)
Security¶
User isolation: Users see only their own media
Spam filtering: Naive Bayes for comments
CSRF protection: Django middleware
XSS prevention: Template auto-escaping