django-autocomplete-light: The jQuery inits are not loaded in the correct order in 3.5.1

There’s a lot going on below, but the heart of the problem is that in 3.5.0, /static/admin/js/jquery.init.js is loaded before /static/autocomplete_light/jquery.init.js (which works) and in 3.5.1 it’s the opposite (which fails). Making that one change alone (building with 3.5.0 as the basis) is enough to trigger this failure.

Given Django 3.0.3 with:

INSTALLED_APPS = [
    'corsheaders',
    'dal',
    'dal_select2',
    'django.contrib.admin',
    'django.contrib.auth',
    .....

In 3.5.0, django-autocomplete-light works fine, but in 3.5.1 it doesn’t.

The main console error is Select2: An instance of jQuery or a jQuery-compatible library was not found. Make sure that you are including jQuery before Select2 on your web page. followed by jQuery.Deferred exception: $(...).select2 is not a function TypeError: $(...).select2 is not a function

However, we do load it - but the load order is slightly different between 3.5.0 and 3.5.1. Here’s the header of the admin page in question:

Built with 3.5.0:

<!DOCTYPE html>

<html lang="en-us" >
<head>
<title>Add Organization settings | Django site admin</title>
<link rel="stylesheet" type="text/css" href="/static/admin/css/base.css">
<link rel="stylesheet" type="text/css" href="/static/admin/css/forms.css">


<script type="text/javascript" src="/admin/jsi18n/"></script>
<link href="/static/vendor/select2/dist/css/select2.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/admin/css/autocomplete.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/autocomplete_light/select2.css" type="text/css" media="screen" rel="stylesheet">
<script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
<script type="text/javascript" src="/static/admin/js/core.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/select2.full.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/i18n/en.js"></script>
<script type="text/javascript" src="/static/admin/js/actions.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/autocomplete.init.js"></script>
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/forward.js"></script>
<script type="text/javascript" src="/static/admin/js/prepopulate.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/select2.js"></script>
<script type="text/javascript" src="/static/admin/js/vendor/xregexp/xregexp.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/jquery.post-setup.js"></script>

.....

Built with 3.5.1:

<!DOCTYPE html>

<html lang="en-us" >
<head>
<title>Add Organization settings | Django site admin</title>
<link rel="stylesheet" type="text/css" href="/static/admin/css/base.css">
<link rel="stylesheet" type="text/css" href="/static/admin/css/forms.css">


<script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.js"></script>
<script type="text/javascript" src="/admin/jsi18n/"></script>
<link href="/static/vendor/select2/dist/css/select2.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/admin/css/autocomplete.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/autocomplete_light/select2.css" type="text/css" media="screen" rel="stylesheet">
<script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/select2.full.js"></script>
<script type="text/javascript" src="/static/admin/js/core.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/i18n/en.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/autocomplete.init.js"></script>
<script type="text/javascript" src="/static/admin/js/actions.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/forward.js"></script>
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/select2.js"></script>
<script type="text/javascript" src="/static/admin/js/prepopulate.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/jquery.post-setup.js"></script>
<script type="text/javascript" src="/static/admin/js/vendor/xregexp/xregexp.js"></script>

.....

Things work fine with 3.5.0. If you use 3.5.1 things break, and if you then substitute the 3.5.0 header (via Chrome dev tools overrides) it’ll work again.

About this issue

  • Original URL
  • State: open
  • Created 4 years ago
  • Reactions: 16
  • Comments: 22 (11 by maintainers)

Most upvoted comments

Hello @jpic sorry for closing the issue I pressed the wrong button!

I tried 3.7.0.dev0 im my project (django 3.0.8) and it seems to be working fine (without using any custom media in my admin form). Thank you for the great work !

Thank you all for the great work 👍

⚠️ Disclaimer: ⚠️ Please excuse any mistakes I might have done below. I am quite new to Django and JavaScript, but I am trying to put my two cents in anyway. 😄

In my opinion, the initial issue report seems to highlight the crux of the problem: the absence of any constraints that determine the order of 'admin/js/jquery.init.js' and 'autocomplete_light/jquery.init.js'. As far as I understand, this means that we are relying on internal details in Django’s stable topological sort algorithm, which seems to have been producing undesirable ordering since the removal of 'admin/js/vendor/jquery/jquery.js' in release 3.5.1 (which, ideally, shouldn’t have any effect on the ordering of the aforementioned jquery init files).

I also took a look at the new approach the library seems to be heading into currently in the master branch (that is, the removal of 'autocomplete_light/jquery.init.js', and making the other scripts more lenient on how they access jQuery). However, in my opinion, this shifts the ordering problem to 'admin/js/vendor/select2/select2.full.js' and 'admin/js/jquery.init.js'. With no constrains in place, Django is free to place any of them before the other and dal still requires that the former be loaded before the latter in order to work. Despite that the approach seems to be working at the moment, a seemingly unrelated change might cause Django to change the order later and suddenly break things (similar to what happened in release 3.5.1).

Personally, I worked around this issue in 3.5.1, by adding a Media class to my admin forms/classes that adds the missing constraint:

    class Media:
        js = [
            'admin/js/jquery.init.js',
            'autocomplete_light/jquery.init.js',
        ]

I understand that this is impossible to include in the library, because it breaks Select2WidgetMixin when used outside django-admin. Therefore, I am proposing that the library includes a mixin that adds a similar Media class, and documents that the user is responsible for inheriting this mixin in their admin forms/classes in order to insert the missing constraint (i.e. similar to how the documentation currently instructs the user to make sure jQuery is properly included when using the library outside of the admin).

Thank you very much, any feedback is appreciated…

It’d be good to know if this solves the problem for them too.

In my case (#1143 ), the problem appears in 3.5.1 and continues unsolved, when an inline with a DAL field coexists with an inline with a django admin autocomplete field, even when I provide explicitly the JQuery file in the Media metaclass of the relevant form:

# This does not work in 3.5.1 or in 3.6.0-dev1
class Admin(admin.ModelAdmin):
    inlines = ('WithAutocompleteInline', 'WithDALIline')

Awesome, releasing 3.7.0 this week.

Thanks to @danielmorell for the great work 😃

@jpic So I logged into work and found the discussion we had about this back in Feb, and using that I was able to replicate the same conditions.

3.6.0dev1 seems to work - the only oddity versus 3.5.0 in the inspection window is that there is a failed attempt to load <site>/static/autocomplete_light/jquery.init.js, followed by a successful attempt to load <site>/static/admin/js/jquery.init.js.

Here’s the page header with 3.5.0. (This virtually identical to how 3.5.0 looked when I originally reported this, with the recent addition of “inlines” and the fact that some of these items are minified now):

/static/autocomplete_light/jquery.init.js is present

<!DOCTYPE html>

<html lang="en-us" >
<head>
<title>Add Organization settings | Django site admin</title>
<link rel="stylesheet" type="text/css" href="/static/admin/css/base.css">
<link rel="stylesheet" type="text/css" href="/static/admin/css/forms.css">


<script type="text/javascript" src="/admin/jsi18n/"></script>
<link href="/static/vendor/select2/dist/css/select2.min.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/admin/css/autocomplete.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/autocomplete_light/select2.css" type="text/css" media="screen" rel="stylesheet">
<script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
<script type="text/javascript" src="/static/admin/js/core.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/select2.full.min.js"></script>
<script type="text/javascript" src="/static/admin/js/inlines.min.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/i18n/en.js"></script>
<script type="text/javascript" src="/static/admin/js/actions.min.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/autocomplete.init.js"></script>
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/forward.js"></script>
<script type="text/javascript" src="/static/admin/js/prepopulate.min.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/select2.js"></script>
<script type="text/javascript" src="/static/admin/js/vendor/xregexp/xregexp.min.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/jquery.post-setup.js"></script>

.....

Here’s the page header with 3.6.1dev1 in use:

/static/autocomplete_light/jquery.init.js is ABSENT

<!DOCTYPE html>

<html lang="en-us" >
<head>
<title>Add Organization settings | Django site admin</title>
<link rel="stylesheet" type="text/css" href="/static/admin/css/base.css">
<link rel="stylesheet" type="text/css" href="/static/admin/css/forms.css">

<script type="text/javascript" src="/admin/jsi18n/"></script>
<link href="/static/vendor/select2/dist/css/select2.min.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/admin/css/autocomplete.css" type="text/css" media="screen" rel="stylesheet">
<link href="/static/autocomplete_light/select2.css" type="text/css" media="screen" rel="stylesheet">
<script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.min.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
<script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/select2.full.min.js"></script>
<script type="text/javascript" src="/static/admin/js/core.js"></script>
<script type="text/javascript" src="/static/vendor/select2/dist/js/i18n/en.js"></script>
<script type="text/javascript" src="/static/admin/js/inlines.min.js"></script>
<script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/autocomplete.init.js"></script>
<script type="text/javascript" src="/static/admin/js/actions.min.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/forward.js"></script>
<script type="text/javascript" src="/static/admin/js/urlify.js"></script>
<script type="text/javascript" src="/static/autocomplete_light/select2.js"></script>
<script type="text/javascript" src="/static/admin/js/prepopulate.min.js"></script>
<script type="text/javascript" src="/static/admin/js/vendor/xregexp/xregexp.min.js"></script>

.....
    

I’m going to try more with this this coming week (I want to replicate the original problem before I call this done, and it was a very busy week). I’m pretty sure it’s working, but I want to make sure I’m actually exercising the right part of the system.

I’m curious what the situation is in this comment:

https://github.com/yourlabs/django-autocomplete-light/issues/1143#issuecomment-601919205

It’d be good to know if this solves the problem for them too.