Rejuvenating vs deprecating Form.Media

Something like this would be useful for me as well, I think. This would enable use cases like object-based JSON assets. (I’m currently leveraging json_script for this which is even simpler, but a built-in way to achieve the same thing would be nice)

I’m fully in favor of improving Django’s Script class if it answers real use cases, all the more when it comes from seasoned and long time Django contributors. :+1:

(Thinking out loud.)

Like @codingjoe I have also been continuing my work in this area. I have written down some of it here:

I only just noticed that the fact that browsers want to use a single importmap only means that there are good reasons for considering implementing this in Django core and not in third party packages, since any importmap implementation wants to be the only importmap implementation in the whole project. I wonder if there’s a somewhat easy way to achieve a consensus on what the importmap’s contents should be and how it is defined. I want to use the importmap to load files from third party Django apps, Johannes loads files from node_modules.

Maybe we can wait and see and hope that browser makes will reconsider support for multiple importmaps, but that means that we’ll have to wait (a lot) longer until we can use these features everywhere.

I don’t have a solution or even an idea how to attack this problem yet.

2 Likes

I’m intimately convinced that at some point Django has to be aware of JS files a project uses. I guess we’ll have more or less two separate different cases:

  • JS files that should be present on all pages. We may imagine some sort of JS files registry at AppConfig level.
  • JS files that are specific to some view/template, (introspecting forms media from context??)

Once a template response is able to collect JS files, whatever the source, it should then be able to produce importmaps for the current page.

By the way, thanks a lot for exposing your journey in experimenting all this!

4 Likes

Thanks!

We may imagine some sort of JS files registry at AppConfig level.

I think what’s important to keep in mind is that the importmap doesn’t have to contain all modules or scripts to be useful, only the modules which are imported by other modules.

The importmap I’m using is much more minimal, at this time it could consist of:

<script type="importmap">{"imports": {"django-prose-editor/editor": "/static/django_prose_editor/editor.648bf4ac654c.js"</script>

The case of modules loaded directly in HTML is already made possible (easily) using Script and forms.Media. What’s missing here is the subresource integrity hashes provided by django-esm/esimport, that’s not impossible to solve though. Maybe the hashes could be written to staticfiles.json and be directly available to {% static %} and friends?

1 Like

Wow matthiask,

cool article! Thanks for sharing your thoughts and journey in such detail. :heart_eyes:

I currently double release my packages on NPM and PyPi, but I understand the urge to want to unify this into a single distribution channel.

Once could probably extend Django-ESM to look for package.json files inside installed Django apps.
It should allow 3rd party to ship their entry points and dependencies, while still riding on the backs of established package managers like npm or yarn.

@claudep maybe Django doesn’t need to know the files it can access, but the module entry points that are available. Regarding forms… I am not so sure. We serve all widgets as web components now and import the ES module once to register the custom element. However, that has made the whole asset management in Django obsolete for us.

There might be a future for Django where asset management is less complex, and we require less code to get it done. Which is probably also the only feasible way forward, considering the mountain of legacy we’re pushing in front of us now.

Anyhow, I am happy to keep the conversation going. There is still much more to explore in this field :slight_smile:

Cheers,
Joe

1 Like

Hi! Jumping into this discussion again after discovering Media is back in style with the release of Script. I think it would be nice to go one step further and allow ContextMixin declare their own media too. With the current state of the form rendering API, Forms can declare their own medias with the media property. This allows sourcing in the template like: {% for tag in form.media.render_js %}{{ tag }}{% endfor %}. Ok, this is nice but there are cases where the view may use several Forms and they may appear in the context as something other than form.

What I propose is to add a get_media(**context) method to ContextMixin that returns all the Medias that the view requires.

The only issue I have right now is that get_media(**context) should be called at the very last moment, including after all superclasse’s get_context() have been called so that it gets the full context. Right now, the only implementation I could think of that works is the following:

class ContextMixin:
    """
    A default context mixin that passes the keyword arguments received by
    get_context_data() as the template context.
    """

    extra_context = None

    def __new__(cls):
        """
        Wrapping get_context_data in a decorator here ensures get_media is called at the very last moment
        when all superclasses' get_context_data have been called.
        """

        def patch_get_context_data(get_context_data):
            @wraps(get_context_data)
            def wrapper(*args, **kwargs):
                context = get_context_data(*args, **kwargs)
                context.setdefault("media", obj.get_media(**context))
                return context

            return wrapper

        obj = super().__new__(cls)
        obj.get_context_data = patch_get_context_data(obj.get_context_data)
        return obj

    def get_media(self, **context_data) -> Media:
        return context_data["form"].media if "form" in context_data else Media()

    def get_context_data(self, **kwargs):
        kwargs.setdefault("view", self)
        if self.extra_context is not None:
            kwargs.update(self.extra_context)
        return kwargs

Now I fear that some may object to the idea of live-patching the get_context_data method.

Please let me know if other have a better idea or if they think this is a dumb proposition altogether.

@matthiask I’m reading through your article and DEP and it appears to me that, even though you have initially thought about a solution based on somethings like importmaps.py (similar to urls.py, for instance), your DEP has moved away from this solution. I developed dj-importmap for a client about a year ago an I opted for the importmaps.py solution too. I think it would be a mistake to drop it because it can be easilly parsed by IDEs like PyCharm who can then propose autocompletion in JS files based on that.

Might I use this thread to advertise for a DSF frontend working group, which may help to move the needle on topics just like this?

1 Like