Skip to main content

wagtailmedia

PyPI PyPI downloads Build Status Coverage pre-commit.ci status

A module for Wagtail that provides functionality similar to wagtail.documents module, but for audio and video files.

Requirements

wagtailmedia requires the following:

  • Python (3.10, 3.11, 3.12, 3.13, 3.14)
  • Django (5.2, 6.0, 6.1)
  • Wagtail (7.0, 7.1, 7.2, 7.3, 7.4, 8.0)

Install

Install using pip:

pip install wagtailmedia

wagtailmedia is compatible with Wagtail 7.0 and above. Check out older releases for compatibility with older versions of Wagtail.

Settings

In your settings file, add wagtailmedia to INSTALLED_APPS:

INSTALLED_APPS = [
    # ...
    "wagtailmedia",
    # ...
]

All wagtailmedia settings are defined in a single WAGTAILMEDIA dictionary in your settings file. The defaults are:

# settings.py

WAGTAILMEDIA = {
    "MEDIA_MODEL": "wagtailmedia.Media",  # string, dotted-notation.
    "MEDIA_FORM_BASE": "",  # string, dotted-notation. Defaults to an empty string
    "AUDIO_EXTENSIONS": [
        "aac",
        "aiff",
        "flac",
        "m4a",
        "m4b",
        "mp3",
        "ogg",
        "wav",
    ],  # list of extensions
    "VIDEO_EXTENSIONS": [
        "avi",
        "h264",
        "m4v",
        "mkv",
        "mov",
        "mp4",
        "mpeg",
        "mpg",
        "ogv",
        "webm",
    ],  # list of extensions
    "ENABLE_API_V3": False,  # Wagtail 8.0+ - enable the v3 API endpoints
}

URL configuration

Your project needs to be set up to serve user-uploaded files from MEDIA_ROOT. Your Django project may already have this in place, but if not, add the following snippet to urls.py:

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = (
    [
        # ... the rest of your URLconf goes here ...
    ]
    + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
)

Note that this only works in development mode (DEBUG = True); in production, you will need to configure your web server to serve files from MEDIA_ROOT. For further details, see the Django documentation: Serving files uploaded by a user during development and Deploying static files.

With this configuration in place, you are ready to run ./manage.py migrate to create the database tables used by wagtailmedia.

wagtailmedia loads additional assets for the chooser panel interface. Run ./manage.py collectstatic after the migrations step to collect all the required assets.

Custom Media model

The Media model can be customised. To do this, you need to add a new model to your project that inherits from wagtailmedia.models.AbstractMedia.

Then set the MEDIA_MODEL attribute in the WAGTAILMEDIA settings dictionary to point to it:

# settings.py
WAGTAILMEDIA = {
    "MEDIA_MODEL": "my_app.CustomMedia",
    # ...
}

You can customize the model form used with your Media model using the MEDIA_FORM_BASE setting. It should be the dotted path to the form and will be used as the base form passed to modelform_factory() when constructing the media form.

# settings.py

WAGTAILMEDIA = {
    "MEDIA_FORM_BASE": "my_app.forms.CustomMediaForm",
    # ...
}

Hooks

construct_media_chooser_queryset

Called when rendering the media chooser view, to allow the media listing QuerySet to be customised. The callable passed into the hook will receive the current media QuerySet and the request object, and must return a Media QuerySet (either the original one, or a new one).

from wagtail import hooks


@hooks.register("construct_media_chooser_queryset")
def show_my_uploaded_media_only(media, request):
    # Only show uploaded media
    media = media.filter(uploaded_by_user=request.user)

    return media

How to use

As a regular Django field

You can use Media as a regular Django field. Here’s an example:

from django.db import models

from wagtail.fields import RichTextField
from wagtail.models import Page
from wagtail.admin.panels import FieldPanel

from wagtailmedia.edit_handlers import MediaChooserPanel


class BlogPageWithMedia(Page):
    author = models.CharField(max_length=255)
    date = models.DateField("Post date")
    body = RichTextField(blank=False)
    featured_media = models.ForeignKey(
        "wagtailmedia.Media",
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
        related_name="+",
    )

    content_panels = Page.content_panels + [
        FieldPanel("author"),
        FieldPanel("date"),
        FieldPanel("body"),
        MediaChooserPanel("featured_media"),
    ]

The MediaChooserPanel accepts the media_type keyword argument (kwarg) to limit the types of media that can be chosen or uploaded. At the moment only "audio" (MediaChooserPanel(media_type="audio")) and "video" (MediaChooserPanel(media_type="audio")) are supported, and any other type will make the chooser behave as if it did not get any kwarg.

Name clash with Wagtail

Do not name the field media. When rendering the admin UI, Wagtail uses a media property for its fields’ CSS & JS assets loading. Using media as a field name breaks the admin UI (#54).

In StreamField

You can use Media in StreamField. To do this, you need to add a new block class that inherits from wagtailmedia.blocks.AbstractMediaChooserBlock and implement your own render_basic method.

Here is an example:

from django.db import models
from django.forms.utils import flatatt
from django.utils.html import format_html, format_html_join

from wagtail import blocks
from wagtail.admin.panels import FieldPanel
from wagtail.fields import StreamField
from wagtail.models import Page

from wagtailmedia.blocks import AbstractMediaChooserBlock


class TestMediaBlock(AbstractMediaChooserBlock):
    def render_basic(self, value, context=None):
        if not value:
            return ""

        if value.type == "video":
            player_code = """
            <div>
                <video width="{1}" height="{2}" controls>
                    {0}
                    Your browser does not support the video tag.
                </video>
            </div>
            """
        else:
            player_code = """
            <div>
                <audio controls>
                    {0}
                    Your browser does not support the audio element.
                </audio>
            </div>
            """

        return format_html(
            player_code,
            format_html_join(
                "\n", "<source{0}>", [[flatatt(s)] for s in value.sources]
            ),
            value.width,
            value.height,
        )


class BlogPage(Page):
    author = models.CharField(max_length=255)
    date = models.DateField("Post date")
    body = StreamField(
        [
            ("heading", blocks.CharBlock(classname="title", icon="title")),
            ("paragraph", blocks.RichTextBlock(icon="pilcrow")),
            ("media", TestMediaBlock(icon="media")),
        ]
    )

    content_panels = Page.content_panels + [
        FieldPanel("author"),
        FieldPanel("date"),
        FieldPanel("body"),
    ]

You can also use audio or video-specific choosers:

# ...
from wagtail.models import Page
from wagtail.fields import StreamField
from wagtailmedia.blocks import AudioChooserBlock, VideoChooserBlock


class BlogPage(Page):
    # ...

    body = StreamField(
        [
            # ... other block definitions
            ("audio", AudioChooserBlock()),
            ("video", VideoChooserBlock()),
        ]
    )

API

v2

To expose media items in the API, you can follow the Wagtail documentation guide for API configuration with wagtailmedia specifics:

# api.py
from wagtail.api.v2.router import WagtailAPIRouter
from wagtailmedia.api.views import MediaAPIViewSet


# Register the router
api_router = WagtailAPIRouter("wagtailapi")
# add any other enpoints you need, plus the wagtailmedia one
api_router.register_endpoint("media", MediaAPIViewSet)

v3 (preview)

Starting with version 8.0, Wagtail provides an experimental API v3, powered by django-ninja. To opt-in, set ENABLE_API_V3 to True in the WAGTAILMEDIA setting. The wagtailmedia endpoint will be available under the <v3 API root>/media/.

For further details, explore the Wagtail API v3 documentation.

Translations

wagtailmedia has translations in Chinese, French, German, Romanian, Ukrainian. More translations welcome!

Contributing

All contributions are welcome!

Upgrading

When upgrading the Wagtail version, it is good practice to also check that the template styles and formatting are up-to-date with the current supported version of Wagtail.

The following templates should be checked:

  • src/wagtailmedia/templates/wagtailmedia/media/add.html
  • src/wagtailmedia/templates/wagtailmedia/media/confirm_delete.html
  • src/wagtailmedia/templates/wagtailmedia/media/edit.html
  • src/wagtailmedia/templates/wagtailmedia/media/index.html
  • src/wagtailmedia/templates/wagtailmedia/media/media_chooser.html
  • src/wagtailmedia/templates/wagtailmedia/media/media_permissions_formset.html
  • src/wagtailmedia/templates/wagtailmedia/media/usage.html

Install

To make changes to this project, first clone this repository:

git clone git@github.com:torchbox/wagtailmedia.git
cd wagtailmedia

With your preferred virtualenv activated, install testing dependencies:

pip install -e '.[testing]' -U

pre-commit

Note that this project uses pre-commit. To set up locally:

# if you don't have it yet, globally
$ pip install pre-commit
# go to the project directory
$ cd wagtailmedia
# initialize pre-commit
$ pre-commit install

# Optional, run all checks once for this, then the checks will run only on the changed files
$ pre-commit run --all-files

How to run tests

Now you can run tests as shown below:

tox

or, you can run them for a specific environment tox -e python3.13-django5.2-wagtail7.0 or specific test tox -e python3.14-django5.2-wagtail7.0 -- tests.test_views.TestMediaChooserUploadView

To run the test app interactively, use tox -e interactive, visit http://127.0.0.1:8020/admin/ and log in with admin/changeme.

Release files for wagtailmedia 0.19.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for wagtailmedia 0.19.1
File Size Uploaded
wagtailmedia-0.19.1.tar.gz 59.4 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for wagtailmedia 0.19.1
File Interpreter ABI Platform
wagtailmedia-0.19.1-py3-none-any.whl Python 3 none any Details

Total release size: 154.0 kB

Release files / wagtailmedia-0.19.1.tar.gz

Download URL wagtailmedia-0.19.1.tar.gz
Size 59.4 kB
Tags Source
SHA-256 checksum
How to use checksums
692ba48f19d15ec10e2fed341e429d727a3db631d6ec228d77911238420f9491
BLAKE2b-256 checksum
How to use checksums
5799b29081688c7704f79443bc61a6da6d2befef14e98afea809e87016f0c28f
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release files / wagtailmedia-0.19.1-py3-none-any.whl

Download URL wagtailmedia-0.19.1-py3-none-any.whl
Size 94.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
775f7be57df349e3be115c9e84ecd6a18049594eca5a5778f418302b7c44decd
BLAKE2b-256 checksum
How to use checksums
8a78d7fd38090526044d6daa91487b3837a29386a72d2929c7965babf18ea310
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 11, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.19.1 This release

2 release files

0.18.1

2 release files

0.17.2

2 release files

0.17.0

2 release files

0.15.2

2 release files

0.15.1

2 release files

0.14.3

2 release files

0.14.0

2 release files

0.13.0

2 release files

0.10.1

2 release files

0.10.0

1 release file

0.9.0

2 release files

0.8.0

2 release files

0.7.1

2 release files

0.7.0

2 release files

0.6.0

2 release files

0.5.0

2 release files

0.4.0

2 release files

0.3.1

1 release file

0.3.0

1 release file

0.2.0

1 release file

0.1.5

1 release file

0.1.4

1 release file

0.1.3

1 release file

0.1.2

1 release file

0.1.1

1 release file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page