Search code examples
djangodjango-formsdjango-widget

Django Media assets in Forms, how to defer/async JS assets?


The official docs explain how to automatically adds assets for certain widgets., from their example:

from django import forms

class CalendarWidget(forms.TextInput):
    class Media:
        css = {
            'all': ('pretty.css',)
        }
        js = ('animations.js', 'actions.js')

What it does not describe is how to make JS assets deferred or async loaded, e.g.

<script defer src="https://myserver.com/static/animations.js">/script>

How do I add the defer or async attribute?


Solution

  • You can override js-renderer of the widget:

    forms.py

    def render_js(cls):
        return [
            format_html(
                '<script defer src="{}"></script>',
                cls.absolute_path(path)
            ) for path in cls._js
        ]
    forms.widgets.Media.render_js = render_js
    

    However, it will be relevant for all widgets of your application.

    UPD: But if you need a different rendering per Widget, I can suggest you the following trick (use 'private' property to define a format):

    class CalendarWidget(forms.TextInput):
        class Media:
            css = {
                'all': ('pretty.css',)
            }
            js = ('animations.js', 'actions.js', 'defer')
    
    def render_js(cls):
        fmt = '<script src="{}"></script>'
        formats = {
            'defer': '<script defer src="{}"></script>'
        }
    
        for path in cls._js:
            if path in formats:
                fmt = formats[path]
                break
    
        return [
            format_html(
                fmt,
                cls.absolute_path(path)
            ) for path in cls._js if path not in formats
        ]
    forms.widgets.Media.render_js = render_js