A fresh approach to autocomplete implementations, specially for Django.

Overview
https://badge.fury.io/py/django-autocomplete-light.png https://secure.travis-ci.org/yourlabs/django-autocomplete-light.png?branch=master https://codecov.io/github/yourlabs/django-autocomplete-light/coverage.svg?branch=master

Features

  • Python 2.7, 3.4, Django 2.0+ support (Django 1.11 (LTS), is supported until django-autocomplete-light-3.2.10),
  • Django (multiple) choice support,
  • Django (multiple) model choice support,
  • Django generic foreign key support (through django-querysetsequence),
  • Django generic many to many relation support (through django-generic-m2m and django-gm2m)
  • Multiple widget support: select2.js, easy to add more.
  • Creating choices that don't exist in the autocomplete,
  • Offering choices that depend on other fields in the form, in an elegant and innovative way,
  • Dynamic widget creation (ie. inlines), supports YOUR custom scripts too,
  • Provides a test API for your awesome autocompletes, to support YOUR custom use cases too,
  • A documented automatically tested example for each use case in test_project.

Upgrading

See CHANGELOG..

For v2 users and experts, a blog post was published with plenty of details.

Resources

Comments
  • Hard to select items in IE10 Mobile

    Hard to select items in IE10 Mobile

    The dropdown menus are really hard to tap in IE10 Mobile (Windows Phone 8). I have to zoom way in to successfully select an item. At the default zoom using Bootstrap's responsive CSS package, most of my taps are recognized as clicking the label of the field below. Works fine on Mobile Safari (iOS 6).

    When I get a chance, I'll create an isolated test case, try to debug, and (if I find a solution) submit a patch. Creating a tracking issue for now.

    It may be an interaction between this package and Crispy Forms - I haven't tried without Crispy.

    bug 
    opened by vtbassmatt 52
  • The language file for

    The language file for "./i18n/en-us" could not be automatically loaded. A fallback will be used instead

    On a clean install, Django 1.10, I get the following message:

    The language file for "./i18n/en-us" could not be automatically loaded. A fallback will be used instead

    I see that in the src and dist folders the translation files don´t have the sublanguage suffixes.

    What´s the best practice here?

    opened by busla 30
  • Select list not loading (Similar to issue #68)

    Select list not loading (Similar to issue #68)

    [03:10:11.488] TypeError: $(this).yourlabsWidget is not a function @ http://127.0.0.1:8000/static/autocomplete_light/widget.js:297

    The following error is given by my browsers development console. I have a simple inline form in the admin. I'm using the basic setup from the documents.

    The html is:

    I'm using django 1.4. Everything appears to be loading correctly, but there is no sign of network requests to the autocomplete url.

    I thought I had the test_project working, but now that appears to not be working either. Tried re-installing the project, but it still wont work. However, the navigation part is definitely working.

    UPDATE:

    After reinstalling the autocomplete_light application and the test_project, the test_project is working. However my application is still not.

    opened by jglstewart 29
  • Is there a way to add a

    Is there a way to add a "loading" gif to the input?

    It would be useful to show a "loading" gif to the user into the input text while he's searching, otherwise it seems the request didn't find anything even if it's still running... Sorry if this already exists but I couldn't find it (I'm using version 2).

    opened by yliharma 28
  • Validation for

    Validation for "create new item"

    In my case I want some basic validation before the user can do "create new item".

    Use case: The user enters mail-adresses. I don't want to accept [email protected], but I want to accept [email protected].

    I looked at the source. I guess validation is not possible at the moment.

    What do you think?

    Here is the relevant code:

    
        def post(self, request):
            """Create an object given a text after checking permissions."""
            if not self.has_add_permission(request):
                return http.HttpResponseForbidden()
    
            if not self.create_field:
                raise ImproperlyConfigured()
    
            text = request.POST.get('text', None)
    
            if text is None:
                return http.HttpResponseBadRequest()
    
            result = self.create_object(text)
    
            return http.HttpResponse(json.dumps({
                'id': result.pk,
                'text': six.text_type(result),
            }))
    
    
    opened by guettli 24
  • 3.5.1 new Media order is not safely compatible with ModelAdmin.autocomplete_fields

    3.5.1 new Media order is not safely compatible with ModelAdmin.autocomplete_fields

    In the following setup, one inline uses autocomplete_fields the other inline uses DAL.

    It works in 3.5.0. In order to work in 3.5.1. the Media meta-calss should also be defined in the inline that uses autocomplete_fields.

    class WithDALForm(forms.ModelForm):
        field_with_DAL = forms.ModelMultipleChoiceField(
            ...
            widget=autocomplete.ModelSelect2Multiple()
        )
    
        class Media:
            js = ("//code.jquery.com/jquery-3.4.1.min.js",)
    
    
    class WithDALInline(admin.StackedInline):
        form = WithDALForm
    
    
    class WithAutocompleteInline(admin.StackedInline)
        autocomplete_fields = ('field_1', 'field_2')
    
    
    class Admin(admin.ModelAdmin):
        inlines = ('WithAutocompleteInline', 'WithDALIline')
    

    What follows is the order of the scripts in each case:

    3.5.1 with admin autocomplete (not working):

    <script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
    <script type="text/javascript" src="//code.jquery.com/jquery-3.4.1.min.js"></script>
    <script type="text/javascript" src="/static/admin/js/vendor/select2/select2.full.js"></script>
    <script type="text/javascript" src="/static/vendor/select2/dist/js/select2.full.js"></script>
    <script type="text/javascript" src="/static/admin/js/vendor/select2/i18n/en.js"></script>
    <script type="text/javascript" src="/static/vendor/select2/dist/js/i18n/en.js"></script>
    <script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/autocomplete.init.js"></script>
    <script type="text/javascript" src="/static/admin/js/core.js"></script>
    <script type="text/javascript" src="/static/admin/js/inlines.js"></script>
    <script type="text/javascript" src="/static/admin/js/autocomplete.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/forward.js"></script>
    <script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/select2.js"></script>
    <script type="text/javascript" src="/static/admin/js/collapse.js"></script>
    <script type="text/javascript" src="/static/admin/js/actions.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/jquery.post-setup.js"></script>
    <script type="text/javascript" src="/static/admin/js/urlify.js"></script>
    <script type="text/javascript" src="/static/admin/js/prepopulate.js"></script>
    <script type="text/javascript" src="/static/admin/js/vendor/xregexp/xregexp.js"></script>
    

    3.5.1 without admin autocomplete (working):

    <script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
    <script type="text/javascript" src="//code.jquery.com/jquery-3.4.1.min.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/admin/js/inlines.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/collapse.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>
    

    3.5.0 with admin autocomplete (working):

    <script type="text/javascript" src="/static/admin/js/vendor/jquery/jquery.js"></script>
    <script type="text/javascript" src="//code.jquery.com/jquery-3.4.1.min.js"></script>
    <script type="text/javascript" src="/static/admin/js/vendor/select2/select2.full.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/jquery.init.js"></script>
    <script type="text/javascript" src="/static/admin/js/vendor/select2/i18n/en.js"></script>
    <script type="text/javascript" src="/static/vendor/select2/dist/js/select2.full.js"></script>
    <script type="text/javascript" src="/static/admin/js/jquery.init.js"></script>
    <script type="text/javascript" src="/static/vendor/select2/dist/js/i18n/en.js"></script>
    <script type="text/javascript" src="/static/admin/js/core.js"></script>
    <script type="text/javascript" src="/static/admin/js/inlines.js"></script>
    <script type="text/javascript" src="/static/admin/js/autocomplete.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/autocomplete.init.js"></script>
    <script type="text/javascript" src="/static/admin/js/admin/RelatedObjectLookups.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/forward.js"></script>
    <script type="text/javascript" src="/static/admin/js/collapse.js"></script>
    <script type="text/javascript" src="/static/admin/js/actions.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/select2.js"></script>
    <script type="text/javascript" src="/static/admin/js/urlify.js"></script>
    <script type="text/javascript" src="/static/autocomplete_light/jquery.post-setup.js"></script>
    <script type="text/javascript" src="/static/admin/js/prepopulate.js"></script>
    <script type="text/javascript" src="/static/admin/js/vendor/xregexp/xregexp.js"></script>
    
    opened by raratiru 23
  • Above 1000 relations using 'through', the page takes 5 minutes to load.

    Above 1000 relations using 'through', the page takes 5 minutes to load.

    When I wasn't using through everything was in one field so django didn't create one field for each relation(as of now).

    Now that I'm using 'through' to intermediate the relation django tries to create on input for each of them and thus takes forever.

    The main purpose of library(as I see) is to remove the need to render a whole list and when in the need to add a new item, it won't need to query the whole database, it will get the ones matching a query which is not very harmful.

    Now the problem is back with this rendering problem. Any idea of how I can fix this and make the django admin page load faster?

    Here's a pic for clarification: image

    pending user feedback 
    opened by pbassut 23
  • ModelChoiceFields: Watch changes to 'queryset'

    ModelChoiceFields: Watch changes to 'queryset'

    A ModelChoiceField's 'queryset' attribute may be overwritten after the field has been created, for example in order to limit the choices that are offered. We should also reflect these changes in the autocompletion.

    A common idiom is to override queryset in a view or form, like in this example: https://docs.djangoproject.com/en/dev/ref/forms/fields/#fields-which-handle-relationships. Currently, these overrides are not reflected in the autocomplete. Hence it is possible to select related objects in the autocomplete that aren't part of queryset anymore, resulting in a validation error.

    This patch fixes this by always reflecting changes to queryset in the autocomplete's choices.

    needs-tests 
    opened by jonashaag 23
  • Fixed JS load order issues

    Fixed JS load order issues

    This PR seeks to solve the same problem as PRs #990, #1147, and #1157.

    I could not find any testing / contributing documentation, so please advise if there are any issues with this PR, I can adjust. I spun up the test_project. However, I am not sure what the results are supposed to be. Most things appeared to be working.

    Here is an overview of what I did.

    ❗ Update: The solution described here has been rewritten. See the update below.

    I initialized a NPM project including select2 as a dependency.

    From there, I used a number of JS build tools to bundle all the autocomplete files into a single JS file called autocomplete.js and a minified version autocomplete.min.js.

    The complicated part was handling i18n. I created a build script select2.build.js in the project root. It does two things.

    First, it collects all the Select2 language files, then writes them to src/dal/static/autocomplete_light/i18n/. It does not just copy each language file, it wraps the entire contents of each file in a function dalLoadLanguage(). The function takes a single argument jQuery. This allows us to call the function during the initialization.

    https://github.com/danielmorell/django-autocomplete-light/blob/e717499a4efe69446aaa5a1141cd4c49531c6ac0/src/dal/static/autocomplete_light/autocomplete.init.js#L71-L81

    It also registers and fires a custom event, dal-language-loaded. This is important since the file may be loaded after the main autocomplete.js bundle. I added an event listener that will call the dalLoadLanguage() function when dal-language-loaded is fired.

    The last thing select2.build.js does is concatenate all the JS files into autocomplete.js. It, does not attempt to perform any optimization or modulization on the code. It simply write them all in order into one file.

    Finally, autocomplete.js is minified using UglifyJS.

    The following build steps are now required.

    Install JS dependencies.

    $ npm install
    

    Build JS files.

    $ npm run build
    
    opened by danielmorell 22
  • Select2WidgetMixin Media as a dynamic property

    Select2WidgetMixin Media as a dynamic property

    partially fixes the issue #731.

    corrects partly because, django get_current_language templatetag return a low case string with language code, and this value is commonly used on html tag, like <html lang="pt-br">, see. if html have any html tag with lang="boo-bar-lang" exists, select2 use the value to try load the language files.

    The problem occur when the language is pt-br, sr-cyrl, zh-cn, zh-tw, because to these languages, some i18n files of select2 is not low case, then select2 can not find the files. (pt-BR.js, sr-Cyrl.js, zh-CN.js, zh-TW.js)

    opened by luzfcb 22
  • Widgets not showing up in and out of Admin panel

    Widgets not showing up in and out of Admin panel

    I've just installed Django-Autocomplete-Light and followed the guide on their site. I've setup the autocomplete-view and it works as intended (returning a JASON of autocomplete options). I can't seem to get the widgets to show up, though. in and out of the admin panel.

    Everything compiles without errors but instead the widget I get an empty drop down selection box.

    Image of empty selection box in admin panel

    This is my settings.py:

    INSTALLED_APPS = [
        'dal',
        'dal_select2',
        'django.contrib.admin',
        'django.contrib.auth',
        'django.contrib.contenttypes',
        'django.contrib.sessions',
        'django.contrib.messages',
        'django.contrib.staticfiles',
        'selectable',
        'videos.apps.VideosConfig',
    ]
    

    This is views.py:

    class ActorAutocomplete(autocomplete.Select2QuerySetView):
        def get_queryset(self):
            # Don't forget to filter out results depending on the visitor !
    
            qs = Actor.objects.all()
    
            if self.q:
                qs = qs.filter(name__istartswith=self.q)
    
            return qs
    

    urls.py:

    url(r'^actor-autocomplete/$', views.ActorAutocomplete.as_view(), name='actor-autocomplete'), forms.py:

    class ActorForm(forms.ModelForm):
        class Meta:
            model = Actor
            fields = ('name',)
            widgets = {
                'name': autocomplete.Select(url='videos/actor-autocomplete')
            }
    

    It feels like I'm missing something simple, but I don't know what it is. Perhaps it can't access the static files, but adding 'dal' to installed_app in settings.py should have solved that according to the documentation. I'll appreciate any help!

    I forgot to mention that Actor.name is of type CharField. Perhaps I'm not using the correct widget, but I haven't found one that is specific for CharField.

    opened by mihaelfi 22
  • Add `class Formset(Forward)`

    Add `class Formset(Forward)`

    We have Self(Forward) which forward the field's own value.

    But the is django-autocomplete-light and Django supports Formsets.

    In a Formset, there a field naming convention (on the id and name attributes) that is roughly form-n-field where form is the name of the form and field the name of the field and n the form number in the set (0, 1, 2, 3, ...).

    Formset(Forward) would collect all the values of that field in the formset and submit them as a list. The server side can then use this list conveniently as an exclusion list to prevent for example, any to forms in the formset having the same selection.

    opened by bernd-wechner 0
  • Make unregistered forward handlers fail silently (or with warning) but non-breaking

    Make unregistered forward handlers fail silently (or with warning) but non-breaking

    Currently if you declare a forwarder with forward.JavaScript the named handler must be registered on the client side with yl.registerForwardHandler. If it is not then when the DAL widget is clicked it will display Searching.... and never complete, but on the console will appear this error:

    22:04:42.777 autocomplete_light.js:465 Uncaught TypeError: handler is not a function
        at Object.<anonymous> (autocomplete_light.js:465)
        at Function.each (jquery.js:381)
        at Object.yl.getForwards (autocomplete_light.js:438)
        at jQuery.fn.init.data (select2.js:60)
        at AjaxAdapter.query (select2.full.js:3620)
        at Select2.<anonymous> (select2.full.js:5669)
        at Select2.Observable.invoke (select2.full.js:655)
        at Select2.Observable.trigger (select2.full.js:645)
        at Select2.trigger (select2.full.js:5827)
        at Select2.open (select2.full.js:5851)
        at Select2.toggleDropdown (select2.full.js:5838)
        at DecoratedClass.<anonymous> (select2.full.js:5609)
        at DecoratedClass.Observable.invoke (select2.full.js:655)
        at DecoratedClass.Observable.trigger (select2.full.js:645)
        at HTMLSpanElement.<anonymous> (select2.full.js:1629)
        at HTMLSpanElement.dispatch (jquery.js:5429)
        at HTMLSpanElement.elemData.handle (jquery.js:5233)
    (anonymous) @ autocomplete_light.js:465
    each @ jquery.js:381
    yl.getForwards @ autocomplete_light.js:438
    data @ select2.js:60
    AjaxAdapter.query @ select2.full.js:3620
    (anonymous) @ select2.full.js:5669
    Observable.invoke @ select2.full.js:655
    Observable.trigger @ select2.full.js:645
    Select2.trigger @ select2.full.js:5827
    Select2.open @ select2.full.js:5851
    Select2.toggleDropdown @ select2.full.js:5838
    (anonymous) @ select2.full.js:5609
    Observable.invoke @ select2.full.js:655
    Observable.trigger @ select2.full.js:645
    (anonymous) @ select2.full.js:1629
    dispatch @ jquery.js:5429
    elemData.handle @ jquery.js:5233
    

    This should at worse issue a console warning and still function simply not calling (the unregistered) forward handler.

    This makes for more robust application (the application I had in mind was to declare a handler by default on all my model forms, and only avail myself of it in JS on those I wanted to. But now I need the smarts both client and server side as to which fields will have a forwarder attached or not.

    opened by bernd-wechner 0
  • Javascript isn't correctly initialized when loaded with HTMX

    Javascript isn't correctly initialized when loaded with HTMX

    First - great project, wonderful documentation!

    I am using dal with HTMX to create an autocomplete field in a modal window. (HTMX loads the modal window from a different View/url and appends it to the current page.) When I visit the HTMX URL directly, everything works fine. But, when I use HTMX to append that same working page to my existing page's DOM, the dropdown menu isn't initializing correctly.

    I see a bunch of stuff on Stack Overflow that references this kind of issue, but it's always (?) caused by forgetting lo load Javascript or form tags. I also see #1270 which is the only other reference to HTMX, but this seems to be a bit different than both of those.

    I have a feeling that stuff is getting loaded in the wrong order - that is, HTMX dumps the scripts into the DOM, and they execute on the page before the form has a chance to initialize. I believe this because the form works the second time it's loaded, or the third time if I mess with the order the libraries are loaded in. I am new to HTMX, so it's possible the PEBKAC.

    I was able to hack together a very ugly but functioning solution as follows. In the template that my HTMX view returns, I added jquery:

    <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.1/jquery.min.js"></script>
    {{ form.media }}
    {% load crispy_forms_tags %}
    ... some generic html here ...
    <p>{% crispy form form.helper %}</p>
    

    Then, I hacked autocomplete_light.js to split the load event listener into a separate function so I could call it manually:

    window.addEventListener("load", function () {
        console.log('This is never called.');
        dal_init();
    });
    
    function dal_init() {
        console.log('This gets called twice, I am not sure why.');
        // Check if `django.jQuery` exists otherwise set `django.jQuery` to non namespaced jQuery.
        window.django = window.django || {};
        if (!django.hasOwnProperty('jQuery') && jQuery !== 'undefined') {
            django.jQuery = jQuery;
        }
    ...the rest of this is unchanged except for the ) removed at the end of what used to be window.addEventListener(
    

    Finally, I added this snippet to autocomplete_light.js to call my newly created function:

    $( document ).ready(function() {
        dal_init();
    });
    

    This works, every time, as far as I can tell. I have already invested too much time in this to spend more time figuring out the root cause and putting together a PR, but maybe this will help someone else who is using this with HTMX (most likely me after I upgrade DAL in a year or two and my hacks break).

    Here is my complete autocomplete_light.js file:

    /*!
     * Django Autocomplete Light
     */
    
    var yl = yl || {};
    yl.functions = yl.functions || {};
    /**
     * Register your own JS function for DAL.
     *
     * @param name The name of your function. This should be the same as the widget
     *             `autocomplete_function` property value.
     * @param func The callback that will initialize your custom autocomplete.
     */
    yl.registerFunction = function (name, func) {
        if (this.functions.hasOwnProperty(name)) {
            // This function already exists to show an error and skip.
            console.error('The DAL function "' + name + '" has already been registered.');
            return
        }
        if (typeof func != 'function') {
            // It's not a function kill it.
            throw new Error('The custom DAL function must be a function.');
        }
        this.functions[name] = func;
        var event = new CustomEvent('dal-function-registered.' + name, {detail: {name: name, func: func}})
        window.dispatchEvent(event);
    };
    
    
    window.addEventListener("load", function () {
        console.log('This is never called.');
        dal_init();
    });
    
    function dal_init() {
        console.log('This gets called twice, I am not sure why.');
        // Check if `django.jQuery` exists otherwise set `django.jQuery` to non namespaced jQuery.
        window.django = window.django || {};
        if (!django.hasOwnProperty('jQuery') && jQuery !== 'undefined') {
            django.jQuery = jQuery;
        }
    
        (function ($) {
            $.fn.getFormPrefix = function () {
                /* Get the form prefix for a field.
                 *
                 * For example:
                 *
                 *     $(':input[name$=owner]').getFormsetPrefix()
                 *
                 * Would return an empty string for an input with name 'owner' but would return
                 * 'inline_model-0-' for an input named 'inline_model-0-owner'.
                 */
                var parts = $(this).attr('name').split('-');
                var prefix = '';
    
                for (var i in parts) {
                    var testPrefix = parts.slice(0, -i).join('-');
                    if (!testPrefix.length) continue;
                    testPrefix += '-';
    
                    var result = $(':input[name^=' + testPrefix + ']')
    
                    if (result.length) {
                        return testPrefix;
                    }
                }
    
                return '';
            }
    
            $.fn.getFormPrefixes = function () {
                /*
                 * Get the form prefixes for a field, from the most specific to the least.
                 *
                 * For example:
                 *
                 *      $(':input[name$=owner]').getFormPrefixes()
                 *
                 * Would return:
                 * - [''] for an input named 'owner'.
                 * - ['inline_model-0-', ''] for an input named 'inline_model-0-owner' (i.e. nested with a nested inline).
                 * - ['sections-0-items-0-', 'sections-0-', ''] for an input named 'sections-0-items-0-product'
                 *   (i.e. nested multiple time with django-nested-admin).
                 */
                var parts = $(this).attr('name').split('-').slice(0, -1);
                var prefixes = [];
    
                for (i = 0; i < parts.length; i += 2) {
                    var testPrefix = parts.slice(0, -i || parts.length).join('-');
                    if (!testPrefix.length)
                        continue;
    
                    testPrefix += '-';
    
                    var result = $(':input[name^=' + testPrefix + ']')
    
                    if (result.length)
                        prefixes.push(testPrefix);
                }
    
                prefixes.push('');
    
                return prefixes;
            }
    
            /*
             * This ensures the Language file is loaded and passes it our jQuery.
             */
            if (typeof dalLoadLanguage !== 'undefined') {
                dalLoadLanguage($);
            } else {
                document.addEventListener('dal-language-loaded', function (e) {
                    // `e.lang` is the language that was loaded.
                    dalLoadLanguage($);
                })
            }
    
            // Fire init event for yl.registerFunction() execution.
            var event = new CustomEvent('dal-init-function');
            document.dispatchEvent(event);
    
            var initialized = [];
    
            $.fn.excludeTemplateForms = function() {
                // exclude elements that contain '__prefix__' in their id
                // these are used by django formsets for template forms
                return this.not('[id*=__prefix__]').filter(function() {
                    // exclude elements that contain '-empty-' in their ids
                    // these are used by django-nested-admin for nested template formsets
                    // note that the filter also ensures that 'empty' is not actually the related_name for some relation
                    // by ensuring that it is not surrounded by numbers on both sides
                    return !this.id.match(/-empty-/) || this.id.match(/-\d+-empty-\d+-/);
                });
            }
    
            /**
             * Initialize a field element. This function calls the registered init function
             * and ensures that the element is only initialized once.
             *
             * @param element The field to be initialized
             */
            function initialize(element) {
                if (typeof element === 'undefined' || typeof element === 'number') {
                    element = this;
                }
    
                // Ensure element is not already initialized.
                if (initialized.indexOf(element) >= 0) {
                    return;
                }
    
                // The DAL function to execute.
                var dalFunction = $(element).attr('data-autocomplete-light-function');
    
                if (yl.functions.hasOwnProperty(dalFunction) && typeof yl.functions[dalFunction] == 'function') {
                    // If the function has been registered call it.
                    yl.functions[dalFunction]($, element);
                } else if (yl.functions.hasOwnProperty(dalFunction)) {
                    // If the function exists but has not been registered wait for it to be registered.
                    window.addEventListener('dal-function-registered.' + dalFunction, function (e) {
                        yl.functions[dalFunction]($, element);
                    })
                } else {
                    // Otherwise notify that the function should be registered.
                    console.warn('Your custom DAL function "' + dalFunction + '" uses a deprecated event listener that will be removed in future versions. https://django-autocomplete-light.readthedocs.io/en/master/tutorial.html#overriding-javascript-code')
                }
    
                // Fire init event for custom function execution.
                // DEPRECATED
                $(element).trigger('autocompleteLightInitialize');
    
                // Add element to the array of already initialized fields
                initialized.push(element);
    
                // creates and dispatches the event to notify of the initialization completed
                var dalElementInitializedEvent = new CustomEvent("dal-element-initialized", {
                    detail: {
                        element: element,
                    }
                });
    
                document.dispatchEvent(dalElementInitializedEvent);
            }
    
            if (!window.__dal__initialize) {
                window.__dal__initialize = initialize;
    
                $(document).ready(function () {
                    $('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize);
                });
    
                if ('MutationObserver' in window) {
                    new MutationObserver(function (mutations) {
                        var mutationRecord;
                        var addedNode;
    
                        for (var i = 0; i < mutations.length; i++) {
                            mutationRecord = mutations[i];
    
                            if (mutationRecord.addedNodes.length > 0) {
                                for (var j = 0; j < mutationRecord.addedNodes.length; j++) {
                                    addedNode = mutationRecord.addedNodes[j];
    
                                    $(addedNode).find('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize);
                                }
                            }
                        }
    
                    }).observe(document.documentElement, {childList: true, subtree: true});
                } else {
                    $(document).on('DOMNodeInserted', function (e) {
                        $(e.target).find('[data-autocomplete-light-function]').excludeTemplateForms().each(initialize);
                    });
                }
            }
    
            // using jQuery
            function getCookie(name) {
                var cookieValue = null;
                if (document.cookie && document.cookie != '') {
                    var cookies = document.cookie.split(';');
                    for (var i = 0; i < cookies.length; i++) {
                        var cookie = $.trim(cookies[i]);
                        // Does this cookie string begin with the name we want?
                        if (cookie.substring(0, name.length + 1) == (name + '=')) {
                            cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
                            break;
                        }
                    }
                }
                return cookieValue;
            }
    
            document.csrftoken = getCookie('csrftoken');
            if (document.csrftoken === null) {
                // Try to get CSRF token from DOM when cookie is missing
                var $csrf = $('form :input[name="csrfmiddlewaretoken"]');
                if ($csrf.length > 0) {
                    document.csrftoken = $csrf[0].value;
                }
            }
        })(django.jQuery);
    
        // Does the same thing as django's admin/js/autocomplete.js, but uses yl.jQuery.
        (function ($) {
            'use strict';
            var init = function ($element, options) {
                var settings = $.extend({
                    ajax: {
                        data: function (params) {
                            return {
                                term: params.term,
                                page: params.page,
                                app_label: $element.data('app-label'),
                                model_name: $element.data('model-name'),
                                field_name: $element.data('field-name')
                            };
                        }
                    }
                }, options);
                $element.select2(settings);
            };
    
            $.fn.djangoAdminSelect2 = function (options) {
                var settings = $.extend({}, options);
                $.each(this, function (i, element) {
                    var $element = $(element);
                    init($element, settings);
                });
                return this;
            };
    
            $(function () {
                // Initialize all autocomplete widgets except the one in the template
                // form used when a new formset is added.
                $('.admin-autocomplete').not('[name*=__prefix__]').djangoAdminSelect2();
            });
    
            $(document).on('formset:added', (function () {
                return function (event, $newFormset) {
                    return $newFormset.find('.admin-autocomplete').djangoAdminSelect2();
                };
            })(this));
        }(django.jQuery));
    
        (function ($, yl) {
            yl.forwardHandlerRegistry = yl.forwardHandlerRegistry || {};
    
            yl.registerForwardHandler = function (name, handler) {
                yl.forwardHandlerRegistry[name] = handler;
            };
    
            yl.getForwardHandler = function (name) {
                return yl.forwardHandlerRegistry[name];
            };
    
            function getForwardStrategy(element) {
                var checkForCheckboxes = function () {
                    var all = true;
                    $.each(element, function (ix, e) {
                        if ($(e).attr("type") !== "checkbox") {
                            all = false;
                        }
                    });
                    return all;
                };
    
                if (element.length === 1 &&
                    element.attr("type") === "checkbox" &&
                    element.attr("value") === undefined) {
                    // Single checkbox without 'value' attribute
                    // Boolean field
                    return "exists";
                } else if (element.length === 1 &&
                    element.attr("multiple") !== undefined) {
                    // Multiple by HTML semantics. E. g. multiple select
                    // Multiple choice field
                    return "multiple";
                } else if (checkForCheckboxes()) {
                    // Multiple checkboxes or one checkbox with 'value' attribute.
                    // Multiple choice field represented by checkboxes
                    return "multiple";
                } else {
                    // Other cases
                    return "single";
                }
            }
    
            /**
             * Get fields with name `name` relative to `element` with considering form
             * prefixes.
             * @param element the element
             * @param name name of the field
             * @returns jQuery object with found fields or empty jQuery object if no
             * field was found
             */
            yl.getFieldRelativeTo = function (element, name) {
                var prefixes = $(element).getFormPrefixes();
    
                for (var i = 0; i < prefixes.length; i++) {
                    var fieldSelector = "[name=" + prefixes[i] + name + "]";
                    var field = $(fieldSelector);
    
                    if (field.length) {
                        return field;
                    }
                }
    
                return $();
            };
    
            /**
             * Get field value which is put to forwarded dictionary
             * @param field the field
             * @returns forwarded value
             */
            yl.getValueFromField = function (field) {
                var strategy = getForwardStrategy(field);
                var serializedField = $(field).serializeArray();
    
                if ((serializedField == false) && ($(field).prop('disabled'))) {
                    $(field).prop('disabled', false);
                    serializedField = $(field).serializeArray();
                    $(field).prop('disabled', true);
                }
    
                var getSerializedFieldElementAt = function (index) {
                    // Return serializedField[index]
                    // or null if something went wrong
                    if (serializedField.length > index) {
                        return serializedField[index];
                    } else {
                        return null;
                    }
                };
    
                var getValueOf = function (elem) {
                    // Return elem.value
                    // or null if something went wrong
                    if (elem.hasOwnProperty("value") &&
                        elem.value !== undefined
                    ) {
                        return elem.value;
                    } else {
                        return null;
                    }
                };
    
                var getSerializedFieldValueAt = function (index) {
                    // Return serializedField[index].value
                    // or null if something went wrong
                    var elem = getSerializedFieldElementAt(index);
                    if (elem !== null) {
                        return getValueOf(elem);
                    } else {
                        return null;
                    }
                };
    
                if (strategy === "multiple") {
                    return serializedField.map(
                        function (item) {
                            return getValueOf(item);
                        }
                    );
                } else if (strategy === "exists") {
                    return serializedField.length > 0;
                } else {
                    return getSerializedFieldValueAt(0);
                }
            };
    
            yl.getForwards = function (element) {
                var forwardElem,
                    forwardList,
                    forwardedData,
                    divSelector,
                    form;
                divSelector = "div.dal-forward-conf#dal-forward-conf-for-" +
                    element.attr("id") + ", " +
                    "div.dal-forward-conf#dal-forward-conf-for_" +
                    element.attr("id");
                form = element.length > 0 ? $(element[0].form) : $();
    
                forwardElem =
                    form.find(divSelector).find('script');
                if (forwardElem.length === 0) {
                    return;
                }
                try {
                    forwardList = JSON.parse(forwardElem.text());
                } catch (e) {
                    return;
                }
    
                if (!Array.isArray(forwardList)) {
                    return;
                }
    
                forwardedData = {};
    
                $.each(forwardList, function (ix, field) {
                    var srcName, dstName;
                    if (field.type === "const") {
                        forwardedData[field.dst] = field.val;
                    } else if (field.type === "self") {
                        if (field.hasOwnProperty("dst")) {
                            dstName = field.dst;
                        } else {
                            dstName = "self";
                        }
                        forwardedData[dstName] = yl.getValueFromField(element);
                    } else if (field.type === "field") {
                        srcName = field.src;
                        if (field.hasOwnProperty("dst")) {
                            dstName = field.dst;
                        } else {
                            dstName = srcName;
                        }
                        var forwardedField = yl.getFieldRelativeTo(element, srcName);
    
                        if (!forwardedField.length) {
                            return;
                        }
    
                        forwardedData[dstName] = yl.getValueFromField(forwardedField);
                    } else if (field.type === "javascript") {
                        var handler = yl.getForwardHandler(field.handler);
                        forwardedData[field.dst || field.handler] = handler(element);
                    }
    
                });
                return JSON.stringify(forwardedData);
            };
    
        })(django.jQuery, yl);
    }
    $( document ).ready(function() {
        dal_init();
    });
    
    opened by iragm 0
  • DAL inside Wagtail with Telepath

    DAL inside Wagtail with Telepath

    Hello. I've search the docs and the issues and the entire web, but without any luck. Has anyone managed to get DAL working with Wagtail / Telepath? Anyone got any pointers? Thanks!

    opened by drcongo 2
  • Correctly render selected choice for ListSelect2 grouped fields

    Correctly render selected choice for ListSelect2 grouped fields

    ListSelect2 does not correctly render the selected choices when used with groups.

    This snippet works for groups but I did not make a PR as I'll let the way to detect or declare grouped vs non-grouped to you.

    class LgrGroupedListSelect2(autocomplete.ListSelect2):
    
        def filter_choices_to_render(self, selected_choices):
            # dal.widget.WidgetMixin.filter_choices_to_render does not handle correctly grouped lists
            ch = []
            for group in self.choices:
                c = [c for c in group[1] if str(c[0]) in selected_choices]
                ch.extend(c)
            self.choices = ch
    
    opened by j-bernard 2
Releases(2.2.0)
  • 2.2.0(Jun 2, 2015)

    PENDING BREAK

    The good old import autocomplete_light API support will be dropped with Django 1.9. All imports have moved to autocomplete_light.shortcuts and importing autocomplete_light will work until the project is used with Django 1.9.

    To be forward compatible with Django master (>=1.9) support, replace::

    import autocomplete_light
    

    By:

    from autocomplete_light import shortcuts as al
    

    This will also make your scripts a lot shorter.

    CSS BREAK

    We've moved back to pre-1.1.10 CSS positioning. This means appending the autocomplete box to an arbitrary DOM element (body by default) and using calculating the top and bottom attribute in javascript with yourlabs.Autocomplete.fixPosition() pretty much like Django admin's calendar widget does. While blunt, this change should help the widget being more compatible across Django admin themes.

    While this positioning system has been used since around 2005 in Django when Adrian Holovaty open sourced admin media in commit dd5320d, it has never been documented that's it's a good system that works well and there's no reason to break backward compatibility in Django admin for that

    • note to Django admin template customizers.

    JS BREAK

    Javascript yourlabs.Autocomplete object does not bind to the same events as it used too. Event handling has been backported from twitter typeahead and tested on firefox and android (#411).

    PYTHON BREAK

    The form field doesn't call super().validate() anymore and now completely relies on AutocompleteInterface.validate_values(). This was how django-autocomplete-light was initially designed for, kudos to @zhiyajun11 for pointing it out ! This optimises code which was doing validation twice and gives the flexibility it was initially designed for from within the Autocomplete class (#410).

    SQL BREAK

    Model Autocompletes now generate custom SQL be able to save the order in which users have filled an autocomplete field. This actually comes from the last 2.x version.

    CHANGES

    Most users won't notice the break except maybe the CSS ones and of course also for Django 1.9 users.

    • #419: ANSI SQL compliance (@sbaum)
    • #413: Exception when using models having primary key names different from id.
    • #412: Support models with a pk different than "id" and non-numeric. (@mhuailin)
    • #411: Android compatibility (js bind changes).
    • #410: Removed double validation by not calling suport of Field.validate().
    • #408: Support Django 1.8 change-link.
    • #409: Compatibility with non-autocomplete inputs present in the widget (@SebCorbin)
    • #318: Remove extra spaces rendered in choices.
    • #438: Hide autocomplete on scroll in Firefox because it bugs (temp fix).
    • #432: New bootstrap_modal test_app by @lucky-user.
    • #118: Extracted JS into a standalone jquery-plugin: https://github.com/yourlabs/jquery-autocomplete-light
    • Reduce default latency because hardware is better.
    • #426: handle z-index since we're absolutely-positioning in 2.2.x

    CONTRIBUTING CHANGES

    The JS part has been extracted to be packaged as a standalone jQuery library to get more pull requests on the JS / CSS part. It sounds like a pretty good start in the JS / UI testing and packaging world. Any help there is welcome. CI now has tests against MySQL and PostgreSQL since we're generating custom SQL.

    Again welcome to new contributors @lucky-user @mhuailin and @SebCorbin and thanks all for reporting issues on GitHub with all needed details and forks which make it easy to reproduce.

    And thanks to @blueyed who helped sinking this year's backlog like crazy.

    Source code(tar.gz)
    Source code(zip)
Owner
YourLabs
OSS Hack'n'Dev, we provide all kind of paid services on OSS and sponsor OSS
YourLabs
Strawberry-django-plus - Enhanced Strawberry GraphQL integration with Django

strawberry-django-plus Enhanced Strawberry integration with Django. Built on top

BLB Ventures 138 Dec 28, 2022
Repo for All the Assignments I have to submit for Internship Application !😅

Challenges Repository for All the Assignments I have to submit for Internship Application ! 😅 As You know, When ever We apply for an Internship, They

keshav Sharma 1 Sep 08, 2022
:couple: Multi-user accounts for Django projects

django-organizations Summary Groups and multi-user account management Author Ben Lopatin (http://benlopatin.com / https://wellfire.co) Status Separate

Ben Lopatin 1.1k Jan 01, 2023
Quick example of a todo list application using Django and HTMX

django-htmx-todo-list Quick example of a todo list application using Django and HTMX Background Modified & expanded from https://github.com/jaredlockh

Jack Linke 54 Dec 10, 2022
Django Phyton Web Apps template themes

Django Phyton Web Apps template themes Free download source code project for build a modern website using django phyton web apps. Documentation instal

Mesin Kasir 4 Dec 15, 2022
A pickled object field for Django

django-picklefield About django-picklefield provides an implementation of a pickled object field. Such fields can contain any picklable objects. The i

Gintautas Miliauskas 167 Oct 18, 2022
Sistema de tratamento e análise de grandes volumes de dados através de técnicas de Data Science

Sistema de tratamento e análise de grandes volumes de dados através de técnicas de data science Todos os scripts, gráficos e relatórios de todas as at

Arthur Quintanilha Neto 1 Sep 05, 2022
A simple Blog Using Django Framework and Used IBM Cloud Services for Text Analysis and Text to Speech

ElhamBlog Cloud Computing Course first assignment. A simple Blog Using Django Framework and Used IBM Cloud Services for Text Analysis and Text to Spee

Elham Razi 5 Dec 06, 2022
Django model mixins and utilities.

django-model-utils Django model mixins and utilities. django-model-utils supports Django 2.2+. This app is available on PyPI. Getting Help Documentati

Jazzband 2.4k Jan 04, 2023
Blog focused on skills enhancement and knowledge sharing. Tech Stack's: Vue.js, Django and Django-Ninja

Blog focused on skills enhancement and knowledge sharing. Tech Stack's: Vue.js, Django and Django-Ninja

Wanderson Fontes 2 Sep 21, 2022
Sampling profiler for Python programs

py-spy: Sampling profiler for Python programs py-spy is a sampling profiler for Python programs. It lets you visualize what your Python program is spe

Ben Frederickson 9.5k Jan 01, 2023
This "I P L Team Project" is developed by Prasanta Kumar Mohanty using Python with Django web framework, HTML & CSS.

I-P-L-Team-Project This "I P L Team Project" is developed by Prasanta Kumar Mohanty using Python with Django web framework, HTML & CSS. Screenshots HO

1 Dec 15, 2021
Simple XML-RPC and JSON-RPC server for modern Django

django-modern-rpc Build an XML-RPC and/or JSON-RPC server as part of your Django project. Major Django and Python versions are supported Main features

Antoine Lorence 82 Dec 04, 2022
Coltrane - A simple content site framework that harnesses the power of Django without the hassle.

coltrane A simple content site framework that harnesses the power of Django without the hassle. Features Can be a standalone static site or added to I

Adam Hill 58 Jan 02, 2023
A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, celery and redis.

Django Channels Websocket Chatbot A Django chatbot that is capable of doing math and searching Chinese poet online. Developed with django, channels, c

Yunbo Shi 8 Oct 28, 2022
Service request portal on top of Ansible Tower

Squest - A service request portal based on Ansible Tower Squest is a Web portal that allow to expose Tower based automation as a service. If you want

Hewlett Packard Enterprise 183 Jan 04, 2023
Automated image processing for Django. Currently v4.0

ImageKit is a Django app for processing images. Need a thumbnail? A black-and-white version of a user-uploaded image? ImageKit will make them for you.

Matthew Dapena-Tretter 2.1k Dec 17, 2022
A Django GraphQL (Graphene) base template

backend A Django GraphQL (Graphene) base template Make sure your IDE/Editor has Black and EditorConfig plugins installed; and configure it lint file a

Reckonsys 4 May 25, 2022
This is django-import-export module that exports data into many formats

django-import-export This is django-import-export module which exports data into many formats, you can implement this in your admin panel. - Dehydrat

Shivam Rohilla 3 Jun 03, 2021
Per object permissions for Django

django-guardian django-guardian is an implementation of per object permissions [1] on top of Django's authorization backend Documentation Online docum

3.3k Jan 04, 2023