Rejuvenating vs deprecating Form.Media

I worry that we’re trying to do too much here. Writing the DEP requires a lot of time and energy, and then the implementation and testing and consensus reaching hasn’t even happened yet.

What if we switched to just introducing the JS() object from django-js-asset into Django and changed some of the code in Django itself to take advantage of it?

One small example where having JS() would have allowed us to keep a template smaller would be here:

This could have been a JS("admin/js/change_form.js", {"data-model-name": self.model._meta.model_name}) block somewhere in the model admin or model form declaration. (As an aside, the ID could be replaced by using document.currentScript.)

The same applies to the popup response, to the prepopulated fields snippet and so on.

we’re going to deprecate Form.Media.

I really hope that won’t be a result of this process :slight_smile:

Bare scripts and CSS files without bundlers are still useful, or maybe even more useful than they were a few years ago – with htmx and all the great advances made with CSS, JavaScript and vanilla browser APIs.

I’d love to see that added to Django :slight_smile: From my side I’m still interested in researching this more, but I do think the JS object would go a long way in a backwards-compatible way.

From my side – main reason to move slow on the DEP has been browser support for the specific browser APIs I’d like to consider for inclusion in Media, namely import maps and declarative shadow DOM. This is all good now in October 2024 though so I’m planning to get back to this soon™.

Edit: For extra visibility, here are the relevant Wagtail issues that I want to get to before this DEP, both scheduled for me to work on in October - November:


Re deprecation – through this thread we’ve identified active usage of Media in django CMS, Wagtail, feincms3, Mezzanine. And interest from a few of us in modernizing the Media API. Feels like there’s a clear need here, though I suppose the API can clearly be more elegant.

2 Likes

Hi! I’m catching up on this conversation. Can I point you to the thread I opened about integrating importmaps and the project I started to implement this idea?

The main idea behind dj-importmap is to declare importmaps in a Django project in a way that IDEs can comprehend to propose autocolpletion in JS files if that becomes the Django standard. dj-importmap relies on a importmaps.py similar to how urls.py works (from a user perspective). I started to use it on a project for a client and I’d love to see the community get behind it, expand it with features and adopt it (or another) as the standard.

Also, related to this, I’d really love a mechanism for templates to declare assets so the view can collect them and include them only once, quite similar to what Media proposes for Form. It is not rare to me to get in the situation where I’d like to {% include %} a partial — potentially more than once — in a template but that partial requires some JS code to be useful. In that situation, the only solution I have is to leave a comment on the template to indicate that these assets also need to be included in the template, which is not conform to the modern practice of declaring self-sufficient components.

I don’t know how complex it is to develop, but I imagine an {% asset %} tag that would do for templates what Media does for form. That tag would be treated differently to other tag and processed at the very end of rendering so that all assets declared by all partials are collected and made available for inclusion in the HTML.

1 Like

Hi hristophehenry,

I’d love to combine forces on this. I wrote Django-ESM a while ago, and we are using it in a company backed production environment.

If you’re interested, please don’t hesitate to ask.

I will probably also address the topic at the next Berlin Django meetup, to gather more ideas from the community.

Cheers!
Joe

Regarding quick wins:

Django’s test suite includes a great example of how to include ES modules in Form.media.js, but it’s only not part of the public API.

It would be a nice start to have a documented way on how to add a <script type="module"> to form media. This is particularly great, if you use web component based custom inputs (Widgets).

I usually use a subclass in my code bases:

class ESM(Asset):
    """A JavaScript asset for ECMA Script Modules (ESM)."""

    def __str__(self):
        path = super().__str__()
        template = '<script src="{}" type="module"></script>'
        return format_html(template, self.absolute_path(path))

Followed by:

class Form:
    class Media:
        js = [ESM("path/to/file.jsm")]

This works nicely, but once could also build a more generic Script asset, that accepts keyword arguments, like so:

class Form:
    class Media:
        js = [Script(src="path/to/file.jsm", type="module")]

That’s already tested and supported by Django and is only missing great documentation.

Small quick win?

Cheers,
Joe

2 Likes

Very much in favour of any small quick win here! The only thing I’d also like to see (and that doesn’t strike me as too big to maybe smuggle in with this) is to support other attributes aswell. The main reason that I’m not using form.media (well, apart from using django-compressor extensively) is that I load >99% of my scripts with <script defer> or <script async>, and I think we should encourage this / make it easy in general.

It should be easy enough (famous last words). But since custom assets have been supported for a couple of Versions now, it probably fine to squeeze it into the LTS before the feature freeze. I’ll draft a ticket.

1 Like

#35886 (Move object-based media assets to the public API) – Django and here it is.

2 Likes

Thanks! I think there’s value in not just supporting standard attributes but also arbitrary data-* attributes, so that additional data can be attached to document.currentScript. I think that’s missing from #35886 currently. Apart from that it’s a good start I think. (Sorry for the silence from my side, I haven’t yet found the time to react to the DEP comments.)

Ok, then, I took some time to properly implement object-based assets as part of the public API:

I tried to keep the scope as narrow as possible, to move this along quickly. I’d love a review, if someone finds the time.

Thanks :pray:

1 Like

This PR is shaping up well. I’d welcome feedback prior to the fast approaching feature freeze :grin:
Perhaps @thibaudcolas @rixx might want to have a look at the PR?

Are we happy with the names of CSS and JS for these MediaAsset classes?
Not sure if having an all-caps class name is something we should avoid. Maybe CSSAsset/JSAsset?

We might also consider their corresponding HTML tags: Script & Link
OR: JavaScipt & Stylesheet

I’d like to but don’t think I can dedicate any time to any PRs in this space for the foreseeable few months. I’d love to follow what others get up to see from the sidelines though :smiling_face:

From my side I’ve done all the necessary research I wanted to do as part of a possible DEP or other similar proposal. I’ll write up my findings properly soon. TL;DR; is I’d recommend Django has built-in support for import maps, most likely via Media, with similar logic to how it handles JS and CSS resources. And add support for all the <script> tag attributes already discussed. For everything else I mentioned (web components, shadow DOM) – feel like the package ecosystem would work fine.

We have gone for Script

I believe the PR is ready to land and will merge it in the new year. If anyone wants to review or give any feedback, now is the time: Fixed #35886 -- Moved object-based Script media assets to the public API by codingjoe · Pull Request #18782 · django/django · GitHub

3 Likes

This has landed in 5.2 - happy testing :grin:

5 Likes

I have continued to work on django-js-asset and projects where I wanted to use importmaps but couldn’t (EDIT: couldn’t because browsers only support one importmap per page right now). The object-based assets blog post may be interesting Object-based assets for Django's forms.Media - Matthias Kestenholz

I’m now thinking that we should replace the forms.Media class with an Assets class (or something) which can collect object-based stylesheets, scripts, import map entries etc., sort them, and output import maps, scripts, and everything. This class could also be made to automatically handle addition with forms.Media objects the correct way.

That definitely sounds like something which requires a DEP. I’ll ponder this some more, and see if I can make some suggestions for DEP 84: Rejuvenate form media by thibaudcolas · Pull Request #84 · django/deps · GitHub

1 Like

Thank you @matthiask! I’ve added my notes in the draft DEP, which I unfortunately have to close and move on from :l I still think this is a great concept for Django, whether as Assets or forms.Media, but I think I need to focus on my involvement with the DSF Board for 2025.

And for now – I’m looking forward to trying out Script!

1 Like

Sorry in advance for spamming on various channels, but I’m really quite excited about the progress I could make here.

I have been working on a small patch to Django which allows more customization when adding forms.Media objects.

I have done some work on the DEP draft by Thibaud and have opened a pull request.

A better way to read it is here deps/draft/0000-rejuvenate-form-media.rst at rejuvenating-form-media · matthiask/deps · GitHub

I also have a working implementation of an ExtendedMedia class which not only allows CSS and JavaScript assets but also allows rendering an aggregated importmap.

All quite nice. Feedback very welcome! I’d very much like to start using import maps so that I can use JavaScript modules with the ManifestStaticFilesStorage without having to process the import statements themselves – delivering the hashed URLs to the browser via an importmap should be sufficient I think.

2 Likes

Hi there,

I have been working like a maniac on serving a large production platform via importmap and ESM.
Finally, to a successful end. I created two packages: django-esm and esimport.

However, I followed a very different approach. I would rather not manage import maps in Django or Python. I opted to use the established package.json specification. Which provides with a better trust chain and automated updates via services like Dependabot. The world doesn’t need another package manager.

If anyone is interested in adopting django-esm, I am looking for testers to create a better documentation. Please don’t hesitate to reach out to me :slight_smile:

If you want to see, what this looks like in production: https://voiio.app/

Best
Joe

And…

…since I have a working importmap now, I want to extend the Script-asset to support entry point loading via an import map.

<script nonce="1234567890" type="module">import '#js/myModule'</script>

However, this requires a change to the current Script implementation. I subclassed Script and ended up with something like this:

class ImportESModule(Script):
    """
    Import ES module inline via an importmap.
    """
    # https://code.djangoproject.com/ticket/36353
    element_template = "<script {attributes}>import '{path}'</script>"

    def __init__(self, **attributes):
        super().__init__(**attributes | {"type": "module"})

    @property
    def path(self):
        return self._path

I created a ticket which needs your support. I think regardless of how you generate your importmap loading scripts via an entry point is a commonly needed feature and could be provided by contrib.staticfiles.

Cheers!
Joe