1

I'm currently working on a template tag that renders forms nicely.

@register.inclusion_tag('myforms.html', takes_context=True)
def myform(context, form)
    context['form'] = form
    context['error_classes'] = 'has-error has-feedback'

    return context

In myforms.html, I use some JavaScript libraries and css files. In the template I added the scripts:

<link rel="stylesheet" href="{% static 'xyz.css' %}"/>
<script lang="javascript" src="{% static 'xyz.js' %}"></script>
<div>
   ...
</div>

When rendering multiple forms on a singe site, those <links> and <script> are multiple times in the html document.

One possibility is to include them in the base template inside the <head> tag. But then they are in the html file even when they aren't needed.

Adding a {{ block head }} to the base template inside the <head> doesn't work either. For this {{ block.super }} is needed to append new js and css files, but {{ block.super }} doesn't work in includes (see https://stackoverflow.com/a/6566463/2014080).

0

2 Answers 2

2

I am a bit curious as to why you use includes. Although they do have some use cases, you should not be using them a lot. Anyway.

Django forms happen to have a feature that enables collecting assets Basically, you should define form-specific media directly on the form, like this:

class MyForm(ModelForm):
    # whatever you have there, then
    class Media:
        css = {
            'screen': ('foo.css', 'bar.css'),
        }
        js = ('jquery.js', 'myform.js')

The point is they are collected by Django, and you can manipulate them in your view or your templatetag, or wherever you have access to all your forms. Suppose you have MyForm and MyOtherForm, you can do:

context['form_media'] = my_form.media + my_other_form.media

And the, in your root template, you can just do:

<head>
    {{ form_media }}
</head>

As long as you stick to this convention in all your views.

Sign up to request clarification or add additional context in comments.

6 Comments

Nice1. We could still argue about the seperation of backend- and frontend-code. Imho media-assets like CSS and JS belong to the frontend and should therefore not be assigned in the backend. Imagine a big developer-team with designated frontend-designers and backend-programmers. Your frontend-team may have no clue about Python but should still be able to add media-assets. But your solution is nice and clean without further dependencies, therefore +1!
I agree. I believe this solution is mostly intended for small js features, such as some form widget wanting to tweak its client-side behavior (Django automatically include any widget-defined media in the form's media). If you're building an actual client-side application, you should probably define an API, probably some rest-like thing, and keep it as two separate projects communicating through that API.
I'm using @register.inclusion_tag to render my forms and for each form I include the same js and css files.. In my template I have something like this: {% myform form %}. I like the idea of using the Media class, I could use a parent class for my forms, but how can I pass the form_media to the global template context to use form_media in the <head>?
@spectras already provided that part: context['form_media'] = my_form.media + my_other_form.media. That code goes into your view-function, where you process your forms.
Ah, okay. Didn't recognize I'd have to add this to every view, thanks.
|
0

django-sekizai should do the trick. With the tags provided by this app you can dynamically add script or media-resources to your document. Be sure to check the docs, too.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.