How to use sekizai's addtoblock tag inside a form widget template

281 views Asked by At

I am writing a custom form field which has a custom widget. The widget needs some static assets. Its template looks like this:

{% load sekizai_tags static %}
<div id="myWidget"> ... </div>

{% addtoblock "css" %}
<link rel="stylesheet" href="{% static 'my_app/css/widget.css' %}">{% endaddtoblock %}
{% addtoblock "js" %}
<script src="{% static 'my_app/js/widget.js' %}"></script>{% endaddtoblock %}

However, this doesn't work. I get: You must enable the 'sekizai.context_processors.sekizai' template context processor or use 'sekizai.context.SekizaiContext' to render your templates.

I guess this is because the form (and widget) has no access to the request object?! Is there a way around this?

I found in the sekizai source a mention of SekizaiContext as...

An alternative context to be used instead of RequestContext in places where no request is available.

... but I can't figure out how exactly I would do that.

Note that I do not want to start passing the request around. This would not be an acceptable solution, as my custom field is meant to be reusable; it should just work in any form without further modifications.

Update

Actually, after having posted an answer to my own question and... a bit of more thinking, I realize that answer - using Media class - does NOT really address the issue. After all, shortcomings of Media class is why sekizai is needed in the first place.

Using Media class would mean:

# widgets.py

class MyWidget(widgets.Widget):
    template_name = "my_widget.html"
    # other stuff

    class Media:
            css = {"all": "my_app/css/widget.css"}
            js = ("my_app/js/widget.js")

And then, adding {{ form.media }} to the template containing the form. Thus, the original intention of posting this question would not have been addressed. i.e. using addtoblock in the widget template so as the field can be used as is, without further modifications to other templates.

In addition, if {{ form.media }} is used the html tags would render immediately after the form, unless if I'd wrap it like so: {% addtoblock "css" %}{{ form.media }}{% endaddtoblock %}. And then both css and js would both be added in the same place (whereas typically one would want to add the css link tag in the head and the js script tag after body).

If anyone has a solution for using sekizai's addtoblock in a template that has no access to the request object, still welcome!

Update 2

I am able to use addtoblock and avoid the context error overriding the get_context method in my widgets.Widget subclass like so:

# widget.py
class MyWidget(widgets.Widget):

def get_context(self, name, value, attrs):
        context = super().get_context(name, value, attrs)
        sezikai_ctx_var = get_varname()
        sekizai_ctx = SekizaiContext().flatten()
        context.update({sezikai_ctx_var: sekizai_ctx[sezikai_ctx_var]})
        return context

However, the assets are still not rendered...

1

There are 1 answers

0
fekioh On

After a bit of thinking, and reading the docs, I realize that static assets for forms and widgets is done by django out-of-the-box:

https://docs.djangoproject.com/en/3.1/topics/forms/media/