Responsive Images#

Responsive images adapt to different screen sizes, ensuring optimal display across devices. This project uses two types of responsive images:

  • Normal Images: Displayed within page content.

  • Thumbnail Images: Shown as thumbnails in content and enlarged in a modal upon clicking.

Image Renditions: For each image, three renditions are created in AVIF and JPEG formats: - 1x Width: Standard size, used in the src attribute of the img tag. - 2x Width: Double size, used provided the image is not larger or nearly the same size as the original. - 3x Width: Triple size, used only when necessary. These renditions are specified in the srcset attribute of the source or img elements.

Those three renditions are put into the srcset attribute of the related source or img elements.

Normal Images#

Normal images fill a slot with a width of 1110px and a maximum height of 740px. But you can configure those values in the settings.

Therefore, if you have an image which is quadratic and has a width of 3000px it will rendered with a maximum width of 740px, but delivered with a width of 2220px to high pixel density devices.

Both AVIF and JPEG formats are supported.

Thumbnail Images#

Thumbnail images have a width of 120px and a maximum height of 80px. But you can also set those values in the settings.

They also support AVIF and JPEG formats.



The blog index page comes with pagination support. You can set the number of posts per page using the POST_LIST_PAGINATION setting.

If there are more then 3 pages, there will be a “…” in the pagination. If there are more then 10 pages, there will be two “…” in the pagination.


The file sizes of an audio object are cached automatically. But for old audio objects there’s an admin action where you can update the file size cache for all associated audio files.

Show the admin action to update the file size cache

Social Media#


If you share a link to a detail page of an episode, there will be a Twitter Player Card added to your tweet. At the moment you have to generate the metadata manually. For an example have a look at this episode template.

Image of a Twitter Card


You can enable / disable comments on app, blog and post-level. For app-level, there’s a global switch you can use in the settings. Blog and post models have a comments_enabled database field. They are set to True by default.


The ajax-calls django-fluent-comments does depend on the availability of a full jquery version.

Comment Spam Filter#

There’s a simple Naive Bayes-based spam filter for comments built in. It’s not very smart, but it’s good enough to filter out most spam. It’s also very easy to train and very fast to run. And it’s only slightly above one hundred lines of pure Python code.

A comment is considered ham if it’s public and not removed. All other comments are considered spam. It’s possible to re-train the spam filter via a Django Admin action on the SpamFilter model. The precision, recall and F1 performance indicators are also shown in the admin interface.

Show a spam filter row in the Django admin interface


Blog / Podcast Author#

If you set the custom CharField field named author on a Blog-Page using the Wagtail or Django-Admin interface, the content of this field is then used to populate following attributes in the feed:

  • itunes:author

  • itunes:name

  • author_name in atom feed

If the author-field is not set blog.owner.get_full_name() is used instead.

Blog / NoIndex#

If you set the custom BooleanField field named noindex on a Blog-Page using the Wagtail or Django-Admin interface, the Page and all its subpages will be excluded from being indexed by search engines.

Categories / Tags#

This is a beta feature. It is not yet fully implemented. Since I don’t know yet if I will go with tags or categories, I added both and wait which one sticks 😄.


Categories are one way to group posts. They come with their own snippet model so you can add them via the admin interface by clicking on one of the categories. A blog post can have multiple categories and a category can have multiple blog posts. If you want to add a new category, you have to add it using the wagtail admin interface.

Categories might be the right thing if you do not have too many of them and they rarely change.


Tags are another way to group posts. They come with their own link to the taggit tag model. You can add tags to a blog post by using the standard wagtail tag interface. A blog post can have multiple tags and a tag can have multiple blog posts. If you want to add a new tag, there’s a text field with auto completion in the wagtail admin interface.

Tags might be the right thing if you have a lot of them and they change often and you don’t mind having to type them in the admin interface.


You can upload video files to the server and play them back in the browser.

Videos have a title a file field original pointing to the original video file, an image poster and a list of tags.

If no poster is given, the first frame of the video after one second is used as poster.


You can upload audio files to the server and play them back in the browser.

Audio Models#

Audio files are represented by the Audio model. Audio files have a title, a subtitle, tags and four file fields pointing to the file in different audio formats:

  • m4a - AAC, works best on Apple/iOS devices

  • mp3 - MP3, works everywhere, but has no time index so the whole file has to be downloaded before playback can start

  • oga - OGG Vorbis, maybe remove this one because apple is now adding support for opus, too

  • opus - Opus, better quality per bitrate than all other formats, but not as well known as the others

Since podcast feeds only support one audio file per episode, there is one feed per audio format. The feeds are generated automatically and can be found at feed/podcast/<audio_format>/rss.xml.


For playback of audio content Podlove Web Player version 4 is used.


Currently supported features:

  • Chapter marks

  • Download button

Templates / Themes#

You can choose different templates for the entire website or for specific blogs / podcasts. All users can select these templates from the Wagtail admin interface. Individual users can make their own template selections, which are stored in their session.

  • Plain HTML (plain) - This is just a plain HTML template without any CSS

  • Bootstrap 4 (bootstrap4) - This is a template that uses Bootstrap 4 and is currently the default template

  • Bootstrap 5 (bootstrap5) - This is a template that uses Bootstrap 5 and is the theme that I usually use for my own projects

  • Vue.js - This is a template that uses Vue.js and demonstrates how to combine an SPA frontend with django-cast

If you want to use your own templates, you can do so by overwriting the built-in templates or creating a new directory in your project’s templates directory and name it cast/{your_template_name}. Then you can create your own templates in this directory. After all of the following template names are added, you should be able to select your custom template in the Wagtail admin interface.


This is the minimal list of templates that have to be implemented for a template named minimal:

  • cast/minimal/base.html

  • cast/minimal/blog_list_of_posts.html

  • cast/minimal/post.html

  • cast/minimal/post_body.html

  • cast/minimal/episode.html

  • cast/minimal/select_template.html


It’s only possible to create own template themes with template loaders that implement the get_dirs method (FilesystemLoader, CachedLoader). If you want to use a template loader that doesn’t implement the get_dirs method, you have to add it to settings.CAST_CUSTOM_THEMES.


Galleries can have different layouts. Currently there are two layouts:

  • default - This is the default layout which will use the cast/minimal/gallery.html template

  • htmx - Which will use the cast/minimal/gallery_htmx.html template

If you don’t want to implement the htmx layout, you can just copy your cast/minimal/gallery.html template to cast/minimal/gallery_htmx.html.

How to Change the Theme for the whole Site#

This setting can be found at settings > Template base directory:

Set the theme or "template base directory" for the whole site

There’s a context processor that adds the current template base directory aka theme to the context. For convenience it also adds the theme’s base template as a variable to the context to be able to extend it in non Wagtail templates.

Error Views#

If you want to use your own error views, you can do so by creating templates for each error code in your theme’s directory.


This is the list of templates that you can overwrite

  • cast/your_theme/404.html

  • cast/your_theme/500.html

  • cast/your_theme/400.html

  • cast/your_theme/403.html

  • cast/your_theme/403_csrf.html

Since now the error views need to know which theme to use, you have to overwrite the default error views in your project’s root URL-conf:

from cast.views import defaults as default_views_cast

handler404 = default_views_cast.page_not_found
handler500 = default_views_cast.server_error
handler400 = default_views_cast.bad_request
handler403 = default_views_cast.permission_denied

Setting the view for the 403_csrf error is a special case. You have to specify the view in your project’s settings:

# view handling csrf failures
CSRF_FAILURE_VIEW = "cast.views.defaults.csrf_failure"

How to Change the Theme for a Single Blog#

This setting can be found at pages > … > Blog:

Set the theme or "template base directory" for a single blog

How to Change the Theme for an Individual User#

The theme selection for an individual user is stored in request.session and does overwrite blog and site level theme settings.


You can get a list of selectable themes via the cast:api:theme-list endpoint. This endpoint will also show the currently selected theme. If you want to update the selected theme, you can do so via cast:api:theme-update.


The hypermedia endpoints for getting / setting the theme are:

  • cast:theme-list - List of all themes (the currently selected theme is marked)

  • cast:theme-update - Update the theme for the current user

Django-Admin Commands#

There are some management-commands bundled with django-cast. Most of them are dealing with the management of media files.

  • recalc_video_posters: Recalculate the poster images for all videos.

  • media_backup: Backup media files from production to backup storage backend (requires Django >= 4.2).

  • media_sizes: Print the sizes of all media files stored in the production storage backend (requires Django >= 4.2).

  • media_replace: Replace files on production storage backend with versions from local file system (requires Django >= 4.2). This might be useful for videos for which you now have a better compressed version, but you don’t want to generate a new name.

  • media_restore: Restore media files from backup storage backend to production storage backend (requires Django >= 4.2).

  • media_stale: Print the paths of all media files stored in the production storage backend that are not referenced in the database (requires Django >= 4.2).

  • sync_renditions: Create missing renditions for images and remove obsolete ones. Useful if you have changed the rendition sizes in your settings or added new image formats.