Models

Django Cast provides a comprehensive set of models for blogging, podcasting, and media management. The models are organized into several categories: page models, media models, content enhancement, and supporting infrastructure.

Some reference documentation about how the models work.

Page Models

Page models define the content structure and hierarchy using Wagtail’s page tree system.

HomePage

The site’s root page that can optionally redirect to another page.

class HomePage(Page):
    body = StreamField(...)  # Content blocks
    alias_for_page = ForeignKey(Page)  # Optional redirect

Key Features:

  • Can redirect to any other page via alias_for_page

  • Supports basic content blocks (heading, paragraph, image, gallery)

  • Serves as the site root in Wagtail’s page tree

Blog

Container page for blog posts with author information and configuration.

class Blog(Page):
    author = CharField(max_length=255)
    email = EmailField()
    uuid = UUIDField()
    comments_enabled = BooleanField(default=True)
    cover_image = ForeignKey('wagtailimages.Image')
    cover_alt_text = CharField(max_length=255)
    noindex = BooleanField(default=False)
    template_base_dir = CharField(max_length=128)
    subtitle = CharField(max_length=255)
    description = RichTextField()

Key Methods:

  • get_template_base_dir(): Returns the theme directory for templates

  • get_published_posts(): Returns queryset of live child posts

  • get_filterset(): Returns filterset for faceted search

  • get_pagination_context(): Handles pagination of posts

Properties:

  • last_build_date: DateTime of most recent post

  • unfiltered_published_posts: All published posts without filtering

Podcast

Specialized blog for podcasting with iTunes-specific metadata.

class Podcast(Blog):
    itunes_artwork = ForeignKey(ItunesArtWork)
    itunes_categories = CharField()  # JSON field
    keywords = CharField(max_length=255)
    explicit = PositiveSmallIntegerField(choices=EXPLICIT_CHOICES)

Additional Features:

  • Inherits all Blog functionality

  • Adds podcast-specific fields for feed generation

  • itunes_categories_parsed property returns parsed categories

Post

A post is a single blog post. It’s the parent of episodes, too.

Main content model for blog posts with rich media support.

class Post(Page):
    uuid = UUIDField()
    visible_date = DateTimeField()
    comments_enabled = BooleanField()
    cover_image = ForeignKey('wagtailimages.Image')
    cover_alt_text = CharField(max_length=255)
    body = StreamField([
        ("overview", ContentBlock(section="overview")),
        ("detail", ContentBlock(section="detail")),
    ])

    # Media relationships
    images = ManyToManyField(Image)
    videos = ManyToManyField(Video)
    galleries = ManyToManyField(Gallery)
    audios = ManyToManyField(Audio)

    # Categorization
    categories = ParentalManyToManyField(PostCategory)
    tags = ClusterTaggableManager()

StreamField Structure:

The body field contains two sections:

  • overview: Summary content shown on index pages

  • detail: Full content shown on detail pages

Both sections support django-cast’s built-in blocks for text, images, galleries, video, audio, code, and embeds. Projects can append custom blocks per section with CAST_POST_BODY_BLOCKS; those custom blocks are inherited by episodes because episodes use Post.body.

Key Methods:

  • sync_media_ids(): Syncs media from StreamField to relationships

  • get_all_images(): Returns all images including from galleries

  • get_description(): Renders content for feeds and meta tags

  • get_repository(): Returns optimized data repository

Properties:

  • blog: Parent Blog instance

  • has_audio: Boolean indicating audio presence

  • comments_are_enabled: Checks if comments allowed

  • media_lookup: Dict mapping media types to objects

  • podlove_players: Configuration for audio players

Template Logic

Since you can set a base directory for templates, the get_template method is overridden to get the base directory from the request and return the correct template.

To be able to render the description of a post without the base template, there’s a _local_template_name attribute set on the Post class that can be used to override the template name. This is used for example in the get_description method to render the description of the post using the post_body.html template for the feed and the twitter card.

API-Fields

There are some additional fields that can be fetched from the wagtail pages API: * uuid - a unique identifier for the post * visible_date - the date the post is visible, usually used for sorting * comments_enabled - whether comments are enabled for this post * cover_image_poster_url - a resized poster image URL for the Podlove Web Player * body - the body stream field of the post * html_overview - the rendered html of the overview section of the body (used in SPA themes) * html_detail - the rendered html of the overview and detail section of the body (used in SPA themes)

Episode

A special kind of post that has some additional fields and logic.

Specialized Post for podcast episodes with audio requirements.

class Episode(Post):
    podcast_audio = ForeignKey(Audio, on_delete=PROTECT)
    keywords = CharField(max_length=255)
    explicit = PositiveSmallIntegerField(choices=EXPLICIT_CHOICES)
    block = BooleanField(default=False)

Key Features:

  • Requires podcast_audio to be published

  • Inherits all Post functionality

  • Additional iTunes metadata fields

Methods:

  • get_enclosure_url(): Returns audio URL for RSS feed

  • get_enclosure_size(): Returns audio file size

  • get_transcript_or_none(): Returns associated transcript

Properties:

  • podcast: Parent Podcast instance

Media Models

Models for managing various media types with user ownership.

Audio

Comprehensive audio file management with multiple format support.

class Audio(models.Model):
    user = ForeignKey(User)
    duration = DurationField()
    title = CharField(max_length=255)
    subtitle = CharField(max_length=255)

    # Format fields
    m4a = FileField(upload_to='cast_audio/m4a')
    mp3 = FileField(upload_to='cast_audio/mp3')
    oga = FileField(upload_to='cast_audio/oga')
    opus = FileField(upload_to='cast_audio/opus')

    data = JSONField()  # Metadata storage

Key Methods:

  • create_duration(): Calculates duration using ffprobe

  • size_to_metadata(): Caches file sizes

  • get_file_size(format): Returns size for specific format

  • get_chaptermark_data_from_file(): Extracts embedded chapters

Properties:

  • audio: List of available formats for player

  • chapters: Chapter marks formatted for player

  • uploaded_audio_files: Iterator of available formats

ChapterMark

Time-based chapters for audio navigation.

class ChapterMark(models.Model):
    audio = ForeignKey(Audio)
    start = TimeField()
    title = CharField(max_length=512)
    link = URLField(blank=True)
    image = URLField(blank=True)

Features:

  • Defines navigable sections within audio

  • Optional links and images per chapter

  • Custom manager for bulk synchronization

Video

Video file management with automatic poster generation.

class Video(models.Model):
    user = ForeignKey(User)
    title = CharField(max_length=255)
    original = FileField(upload_to='cast_video')
    poster = ImageField(upload_to='cast_video_poster')
    poster_seconds = FloatField(default=0)

Key Methods:

  • create_poster(): Generates thumbnail using ffmpeg

  • get_mime_type(): Returns MIME type from extension

  • _get_video_dimensions(): Extracts video dimensions

Properties:

  • filename: Original filename

  • type: Returns “video”

Image

Extended Wagtail image model with user ownership.

class Image(AbstractImage):
    user = ForeignKey(User)

Features:

  • Inherits Wagtail’s image functionality

  • Adds user ownership for filtering

  • Automatic rendition generation

File

Simple file storage with user association.

class File(models.Model):
    user = ForeignKey(User)
    original = FileField(upload_to='cast_files')

ItunesArtWork

Podcast artwork storage for iTunes requirements.

class ItunesArtWork(models.Model):
    original = ImageField(upload_to='cast_itunes_artwork')
    original_height = PositiveIntegerField()
    original_width = PositiveIntegerField()

Content Enhancement Models

Models that add functionality to core content.

Transcript

Multi-format transcripts for audio accessibility.

class Transcript(models.Model):
    audio = OneToOneField(Audio)
    podlove = FileField(upload_to='cast_transcript/podlove.json')
    vtt = FileField(upload_to='cast_transcript/vtt')
    dote = FileField(upload_to='cast_transcript/dote.json')

Properties:

  • podlove_data: Parsed Podlove format data

  • dote_data: Parsed DOTe format data

  • podcastindex_data: Converted to podcast index format

PostCategory

Category taxonomy for organizing posts.

class PostCategory(models.Model):
    name = CharField(max_length=255, unique=True)
    slug = SlugField(unique=True)

Features:

  • Registered as Wagtail snippet

  • Used for faceted search and filtering

Moderation Models

SpamFilter

Machine learning spam detection for comments.

class SpamFilter(models.Model):
    name = CharField(max_length=128, unique=True)
    model = JSONField()  # Serialized NaiveBayes
    performance = JSONField()  # Metrics

Class Methods:

  • comment_to_message(comment): Converts comment to trainable text

  • get_training_data_comments(): Gets comments for training

  • get_default(): Returns default filter instance

Methods:

  • retrain_from_scratch(): Rebuilds model from all comments

Theme Models

TemplateBaseDirectory

Site-wide theme configuration setting.

@register_setting
class TemplateBaseDirectory(BaseSiteSetting):
    name = CharField(max_length=128, choices=get_choices())

Utility Functions:

  • get_template_base_dir_choices(): Available themes

  • get_template_base_dir(request): Current theme

Repository Models

The repository pattern provides optimized data access with minimal queries.

Public import surface

Import repository helpers from cast.models.repository. The package exports the supported public names for repository classes, serialization helpers, selected builder helpers, and repository type aliases. The example below is a curated subset showing the most common imports, not the full __all__ surface.

from cast.models.repository import (
    BlogIndexContext,
    EpisodeFeedContext,
    FeedContext,
    PostDetailContext,
    PostQuerySnapshot,
    deserialize_audio,
    deserialize_blog,
    deserialize_episode,
    deserialize_image,
    deserialize_post,
    deserialize_renditions,
    deserialize_transcript,
    deserialize_video,
    serialize_audio,
    serialize_blog,
    serialize_episode,
    serialize_image,
    serialize_post,
    serialize_renditions,
    serialize_transcript,
    serialize_video,
)

Additional public exports include builder helpers such as add_queryset_data, data_for_blog_cachable, and get_facet_choices; utility helpers such as cache_page_url and rendition_to_dict; and type aliases such as PostByID, AudioById, and RenditionsForPosts.

The 0.2.54 cleanup removed the older alias names. Use the canonical names below when upgrading existing code:

Removed

Replacement

QuerysetData

PostQuerySnapshot

PostDetailRepository

PostDetailContext

BlogIndexRepository

BlogIndexContext

FeedRepository

FeedContext

EpisodeFeedRepository

EpisodeFeedContext

audio_to_dict

serialize_audio

video_to_dict

serialize_video

image_to_dict

serialize_image

blog_to_dict

serialize_blog

blog_from_data

deserialize_blog

post_to_dict

serialize_post

episode_to_dict

serialize_episode

transcript_to_dict

serialize_transcript

The removed aliases are not kept as compatibility shims. Importing them now raises ImportError or AttributeError depending on how they are accessed.

PostQuerySnapshot

Base repository for post querysets with prefetched relations.

Contains:

  • Posts with all media relationships

  • Image renditions

  • User data

  • URL information

PostDetailContext

Optimized repository for single post pages.

Features:

  • All data for rendering without additional queries

  • Includes parent blog data

  • Media with renditions

BlogIndexContext

Repository for blog index pages with filtering.

Features:

  • Filtered and paginated posts

  • Facet counts for categories, tags, dates

  • Optimized for list views

FeedContext

Specialized repository for RSS/podcast feed generation.

Optimizations:

  • Minimal data for feed rendering

  • Efficient query patterns

  • Supports both blog and podcast feeds

EpisodeFeedContext

Per-episode context used while rendering podcast feed items.

Contains:

  • Episode podcast audio object

  • Optional transcript object for podcast:transcript output

Model Relationships

Page Hierarchy

HomePage
├── Blog
│   ├── Post
│   └── Post
└── Podcast
    ├── Episode
    └── Episode

Media Associations

  • PostAudio/Video/Image/Gallery (ManyToMany)

  • EpisodeAudio (ForeignKey, required)

  • AudioTranscript (OneToOne)

  • AudioChapterMark (OneToMany)

  • GalleryImage (ManyToMany)

User Relationships

  • Audio/Video/File/ImageUser (ForeignKey)

  • PostUser (via Wagtail’s owner)

Categorization

  • PostPostCategory (ManyToMany)

  • Post/Audio/VideoTags (TaggableManager)

Special Behaviors

Media Synchronization

Posts automatically sync media references from built-in StreamField media blocks to ManyToMany relationships using the sync_media_ids() method. This happens on save and ensures built-in image, gallery, video, and audio blocks are properly associated for queries. Media references inside custom CAST_POST_BODY_BLOCKS blocks are not synced automatically.

Rendition Management

Image renditions are automatically created based on usage context. The system supports:

  • Regular image slots with configurable dimensions

  • Gallery-specific renditions

  • Bulk pre-generation for performance

Performance Optimization

The repository pattern aggressively prefetches related data to minimize queries:

  • Single query for post lists with all media

  • Optimized feed generation

  • Cached facet counts

Theme System

Templates are resolved through a flexible hierarchy:

  1. Session-based theme selection

  2. Blog-level configuration

  3. Site-wide default

  4. Built-in templates

Feed Support

Specialized repositories and methods optimize feed generation:

  • Minimal queries for large feeds

  • Proper caching headers

  • Support for both RSS and podcast feeds