How to provide $ to third-party, external jQuery plugins in Django admin
Asked Answered
B

4

12

I've included a couple of third-party jQuery plugins in my Django admin base template which assume "$" to be available.

For my own code, I've always been happy to just do

(function($) {
    my_code = 'here';
})(django.jQuery);

but how can I provide "$" to other people's code which sits in external files?

<script src="{{ STATIC_URL }}js/jquery.json-2.2.min.js" type="text/javascript"></script>

complains that "$" is undefined. I've tried to put

<script type="text/javascript">var $ = django.jQuery;</script>

before that external reference, but to no avail (btw, why is that? I understand loading happens concurrently, but execution? I can use that "$" immediately after defining it.).

I'm happy with the jQuery version that Django admin provides and really don't want to load another one. I also don't want to edit someone else's plugin so that it starts with the above "$" re-definition. EDIT: Neither do I want to wrap it like my own code, I just don't want to touch those files at all.

Do I really have to resort to putting $.getScript() - http://api.jquery.com/jQuery.getScript - into my anonymous function to load such files?

EDIT: After actually looking into that external file jquery.json-2.2.min.js, I saw it was already wrapped into a function that assumed "jQuery" to be available, rather than "$". After inserting

var jQuery = django.jQuery;

before the external reference, it worked fine. But is this really how this should be done?

Bacteriology answered 30/3, 2011 at 9:35 Comment(7)
The re-definition would be doubly wrong, as you're declaring some global variable which has no effect on function parameter names.Intarsia
Sorry, that was just an attempt at a dirty workaround. I've edited the question to clarify that I don't want to touch those external library files at all.Bacteriology
My point is that the dirty workaround wouldn't work. :)Intarsia
The answer by @benzkji seems to be the best solution - https://mcmap.net/q/944383/-how-to-provide-to-third-party-external-jquery-plugins-in-django-adminFremitus
@Fremitus wouldn't that require to define a corresponding Media for every single ModelAdmin?Bacteriology
@DannyW.Adair There is some work to verify that '$' is indeed loaded where it's needed. The main point is to not re-define jQuery and overwrite existing definitions. See e.g. django-ajax-selects' bootstrap script for inspiration. The script validates that the dependencies exist. This is especially noteworthy when you have multiple ModelAdmins that are loaded since the definitions that they make would carry over.Fremitus
@Fremitus (keeping in mind this is from 11 years ago, my last edit shows what worked in the end) I am not sure I understand. The script you linked to basically says "load jquery etc. (only) if they're not there yet". But the Django jquery from the admin is there. It is django.jQuery, and it is "deliberately separate". My question was for the situation "I'm happy with the jQuery version that Django admin provides and really don't want to load another one." So I'm not redefining, I'm defining "jQuery" to be the existing "django.jQuery".Bacteriology
M
6

Override django's staticfile admin/js/jquery.init.js by creating a file with the same name and path in your app's staticfiles directory.

The original content is this

/* Puts the included jQuery into our own namespace using noConflict and passing
 * it 'true'. This ensures that the included jQuery doesn't pollute the global
 * namespace (i.e. this preserves pre-existing values for both window.$ and
 * window.jQuery).
 */
var django = {
    "jQuery": jQuery.noConflict(true)
};

Just remove the .noConflict(true).

Move answered 6/2, 2015 at 10:8 Comment(2)
This creates more problems than it solves. Now all my other jQuery plugins are complaining about jQuery not existing.Agape
My scenario was that we have a bunch of existing JS code that relies on '$' to use jQuery, and we're using a couple of 3rd party widgets which include jquery.init.js. This was breaking our other code. This solution worked perfectly.Steeple
B
1

Yeah, I remember this problem. I feel your pain.

A great workaround is to restructure your js files in such a way that Django can read them as URLs. In your URLs file, add the pattern below:

urlpatterns = patterns((r"^js(?:/(?P<type>\w+))?", "app.views.render_js"))

Now, in your init.py, add the following code:

JS_FILES = {"name" : "name.js",
            "thing" : "thing.js"};

def render_main_js(req, type = None) :
    return render_to_response(JS_FILES.get(type, "main.js"), mimetype="text/javascript");

Once the code is in place and assuming you have your javascript files in /js/* you can include your javascript by using the code below:

<script type="text/javascript" src="/js/name"></script>
<script type="text/javascript" src="/js/thing"></script>
Blood answered 30/3, 2011 at 9:51 Comment(4)
1. Just as I started to like the new staticfiles... 2. Without having tried it out yet, what is the magic difference? Isn't that just dynamically serving up the same static file? Has "$" appeared by that time?Bacteriology
On second thought, no it hasn't. All these problems are client-side.Bacteriology
Start from the top, work your way down from there. First, move your $ = django.Jquery; assignment above everything else, see if that works. If it doesn't, make sure all of your <script> tags are closed, even the includes. If that doesn't work, are you running Firebug/Chrome? Do you have any additional information?Blood
Thank you for helping. Please see my last edit, I have it "working" just wondering now whether this is common/recommended practice.Bacteriology
U
1

For third party plugins, it's usually best to load your own copy of jQuery before including the other plugins. For Django 1.4+ this may look like so in your corresponding admin.py file:

class Media:
    js = (
        'http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js',
        '/static/js/third_party_plugin.js',
    )

If your plugins do not depend on a recent version of jQuery, you may also use Django's included version by defining $ and jQuery at the top of your plugin:

var jQuery = django.jQuery, $ = jQuery;

As of version 1.6, Django will ship with jQuery 1.9.1. Before that, jQuery 1.4 is used, which does not work for a lot of new/updated plugins.

Unstressed answered 14/6, 2013 at 9:50 Comment(0)
L
1

There is a solution that works without double loading of jQuery, and without hacking jquery.init.js. It relies on the order the libraries are loaded in the admin.

Idea

  1. load jquery
  2. load third party libraries that depend on jQuery/$
  3. optional, load your own scripts, that use jQuery/$
  4. load jquery.init.js from Django (does the django.jQuery and noConflict thing)
  5. optional, load your own scripts, that use django.jQuery

Example

Media Attribute of an Admin, including select2.js.

    class Media:
        js = [
            'admin/js/vendor/jquery/jquery.js',
            'admin/js/vendor/select2/select2.full.js',
            'your_own/js/your_own-using-django.jQuery.js',            
            'admin/js/jquery.init.js',
            'your_own/js/your_own-using-django.jQuery.js',
        ]
Luisaluise answered 24/1, 2022 at 14:38 Comment(1)
This should be the highest ranked answer since it doesn't require any hacking or manual assignments etc. Moreover, it is probably a good idea to note that the relevant css should also be loaded, e.g. it could be css = { 'all': ( 'admin/css/vendor/select2/select2.min.css',)}Fremitus

© 2022 - 2024 — McMap. All rights reserved.