Add jquery to widget Django

There is two simple ways to add jQuery to forms or widgets in django admin.

1. Adding of an other version of jQuery

Firstly, you need download jQuery. As an example for this article I used jQuery.2.2.0. Place library, for example, here: my_project/static/jquery/jquery-2.2.0.min.js.

my_app/forms.py
from django import forms
from django.conf import settings

from my_app.models import MyModel


class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        exclude = ()

    class Media:
        js = [settings.JQUERY_URL, 'my_app/my_script.js']
settings.py
import os

...
JQUERY_URL = os.path.join(STATIC_URL, 'jquery/jquery-2.2.0.min.js')
...

2. Usage Django jQuery

my_app/forms.py
from django import forms
from django.conf import settings

from my_app.models import MyModel


class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        exclude = ()

    class Media:
         extra = '' if settings.DEBUG else '.min' # to use minified libraries or not
         js = [f'admin/js/vendor/jquery/jquery{extra}.js',
               'admin/js/vendor/jquery/jquery.init.js'
               'my_app/admin_compat.js',
               'my_app/my_script.js']
my_app/admin_compat.js
if (window.django !== undefined) var jQuery = django.jQuery, $ = django.jQuery;

Depending on whether our code is in development mode or in production, we use the full or minified version of jquery. This must be done, otherwise two versions of jquery will be loaded either on the local machine or on production - this is not only redundant code, but it may also happen that the connected library can work locally, but not on production and vice versa.

admin/js/vendor/jquery/jquery(/.min).js - version of jquery, that is in the box with Django.

admin/js/vendor/jquery/jquery.init.js - renames $ todjango.jQuery to avoid the namespace conflict, if the page uses the different version of jQuery (or even if another library uses the same $ character to shorten the name of its function). That's its code:

var django = django || {};
django.jQuery = jQuery.noConflict(true);

But sense we othen use $ in our scripts, for example:

$('.test_btn').click(function(){
    alert('clicked');
});

then we need to do to the reverse conversion, as it is done in admin_compat.js - adds $ (including jQuery) to the javascript spacename.

We can and do not use admin_compat.js, but in this case we will have to use django jquery like this:

(function('.test_btn').click(function(){
    alert('clicked');
})(django.jQuery);

Also, if we want use $ in our scripts, we should not just use jquery Django without jquery.init.js and admin_compat.js:

class MyModelForm(forms.ModelForm):
    class Meta:
        model = MyModel
        exclude = ()

    class Media:
         extra = '' if settings.DEBUG else '.min'
         # bad usage your scripts without jquery.init.js and admin_compat.js
         js = [f'admin/js/vendor/jquery/jquery{extra}.js', 'my_app/my_script.js']

Because Django can use jquery.init.js when renders another page parts, for example, when renders inline objects. In this case $ will rename to django.jQuery and for your script my_script.js function $ may be undefined.

I also want to remind you that if you use admin template inherited, for example, from base_site.html, you must still explicitly output the variable media:

my_app/views.py
from __future__ import unicode_literals

from django.core import management
from django.shortcuts import redirect, render
from django.views.generic import View

from my_app.forms import MyModelForm


class TestView(View):
    template_name = 'admin/my_app/test_template.html'
    form = MyModelForm

    def get(self, request, *args, **kwargs):
        return render(request, self.template_name, {'form': self.form()})

    def post(self, request, *args, **kwargs):
        form = self.form(request.POST)

        if form.is_valid():
            # some handling...
            return redirect('/')

        return render(request, self.template_name, {'form': form})
my_app/urls.py
from django.conf.urls import url
from django.contrib.auth.decorators import user_passes_test

from my_app.views import TestView


urlpatterns = [
    url(r'^my_test/$', user_passes_test(lambda u: u.is_superuser)(TestView.as_view()), name='my_test'),
]
my_app/templates/admin/my_app/test_template.html
{% extends "admin/base_site.html" %}
{% load i18n %}

{% block title %}Custom admin page{% endblock %}

{% block extrahead %}{{ block.super }}
    <script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
    {{ form.media }}
    {% comment %}Note: you can place your scripts here and manage the order of loading of scripts{% endcomment %}
{% endblock %}

{% block content %}
    <div class="fieldsets">
        <fieldset class="module">
            <h1>Custom admin page</h1>

            <p>Page content</p>
        </fieldset>
    </div>
{% endblock %}

Sense {{ media }} is placed only in change_form.html (and also in delete_confirmation.html and delete_selected_confirmation.html to be precies):

lib/python2.7/site-packages/django/contrib/admin/templates/admin/change_form.html
{% extends "admin/base_site.html" %}
{% load i18n admin_urls static admin_modify sb_tags %}

{% block extrahead %}{{ block.super }}
<script type="text/javascript" src="{% url 'admin:jsi18n' %}"></script>
{{ media }}
{% endblock %}

{% block extrastyle %}{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/forms.css" %}" />{% endblock %}

{% block coltype %}colM{% endblock %}
...

Django admin javascript load order in forms and widgets

The above methods works, when you want to load your scripts to admin. But if there are widgets, that required jQuery, then you need load jQuery before loading of widgets, sense when page is rendered, the widget scripts are first loaded, and then what is declared inMedia of Form class.

So far, I've found the only solution - to extend the widgets, adding jquery to first position in Media class. How to do this, I will show in example with django-select2.

Add jQuery to django-select2

Deprecated!
It is actual for django-select2 <= 7.7.1

If you want to quickly add the Select2 autocomplete to your Select in django admin, you can write like this:

from django_select2.forms import Select2Widget

class MyForm(forms.Form):
    my_choice = forms.ChoiceField(widget=Select2Widget)

But most likely it will not be work. See documentation about jQuery requirement (it is not in the box with widget):

External Dependencies
- jQuery version 2. This is not included in the package since it is expected that in most scenarios this would already be available.

Okey, it seems that we just need to add jQuery to form class like this:

class MyAdminForm(forms.ModelForm):

    class Media:
        model = MyModel
        js = ('path/to/jquery.js', )  # Declared scripts in Media will be added after all scripts of used widgets.

But Select2 will not works! Sense the declared styles and scripts in Media will be added after all styles and scripts of used widgets:

my_app/my_model/1/change/ (viewing page in the browser of the loaded page)
<head>
...
<link href="/static/select2/css/select2.css" type="text/css" media="all" rel="stylesheet">
<link href="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/css/select2.min.css" type="text/css" media="screen" rel="stylesheet">

<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/select2.min.js"></script>
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/select2/4.0.3/js/i18n/ru.js"></script>
<script type="text/javascript" src="/static/django_select2/django_select2.js"></script>

<script type="text/javascript" src="/static/jquery/jquery.js"></script>
...
</head>

To load jquery before select2 scripts, I overrode Media class, created ownSelect2Widget class and inherited its from base class:

my_app/widgets.py
from django_select2.forms import Select2Widget as BaseSelect2Widget

class Select2Widget(BaseSelect2Widget):
    """
    Added jquery to widget
    """
    def _get_media(self):
        media = super()._get_media()

        css = ['select2/css/select2.css']  # if you want override some styles
        css.extend(media._css['screen'])

        extra = '' if settings.DEBUG else '.min'
        js = [f'admin/js/vendor/jquery/jquery{extra}.js', 'admin/js/jquery.init.js', 'my_app/js/admin_compat.js']
        js.extend(media._js)

        return forms.Media(js=js, css={'screen': css})

    media = property(_get_media)

In this code, as you can see, I used jquery, that is in the box with django. If you want to use another version of jQuery, then just replace line:

extra = '' if settings.DEBUG else '.min'
js = [f'admin/js/vendor/jquery/jquery{extra}.js', 'admin/js/jquery.init.js', 'my_app/js/admin_compat.js']

to:

js = ['jquery/jquery.js']

Now you can use Select2Widget, as described in documentation, importing your Select2Widget:

my_app/forms.py
from my_app.widgets import Select2Widget

class MyForm(forms.Form):
   my_choice = forms.ChoiceField(widget=Select2Widget)

Rate this article

5 from 5 (total 1 rating)

You can send feedback, suggestions or comments on this article using this form:

Fields marked by star ( * ) is required.

Thank you for yor feedback!

After clicking the "Send" button, your message will be delivered to me on the mail.

Author of Article

Artem Maltsev

Web-developer, having the knowlenge of programming language - Python, framework - Django, content management system - Django CMS, platform of e-commerce site - Django Shop and many other applications, using this technologies.

The right to use content on this page https://vivazzi.pro/it/add-jquery-to-widget-django/:

Permission is granted to copy an content with its author and reference to the original without using the parameter rel="nofollow" in tag <a>. Usage:

Author of Article: Artem Maltsev
Link to article: <a href="https://vivazzi.pro/it/add-jquery-to-widget-django/">https://vivazzi.pro/it/add-jquery-to-widget-django/</a>

More: Terms of site usage

Comments: 0

You can leave a comment as an unregistered user.

But if you sing up, you can:

  • receive notifications
  • view your comments
  • be able to use all the functions of the developed services

To comment in one's own name you should log in or sign up on Vuspace website

Send

There is no search on this site, so I offer to use usual search engine, for example, Google, adding "vivazzi" after your request.

Try it

Select currency for displaying monetary values