Opened 3 years ago
Closed 3 years ago
#32628 closed New feature (wontfix)
Add extra data to autocomplete request
Reported by: | Seb G | Owned by: | nobody |
---|---|---|---|
Component: | contrib.admin | Version: | 3.2 |
Severity: | Normal | Keywords: | |
Cc: | Johannes Maron | Triage Stage: | Unreviewed |
Has patch: | no | Needs documentation: | no |
Needs tests: | no | Patch needs improvement: | no |
Easy pickings: | no | UI/UX: | no |
Description
More often than not, I need to add extra parameters to requests sent by autocomplete widgets from admin forms. Currently I have 2 options to do so:
- Listen to opening/closing Select2 events to add headers to the request:
$(document).on("select2:opening", "#foo_set-group select.admin-autocomplete", function () { $.ajaxSetup({headers: {"X-Foo": "bar"}}); } ).on("select2:closing", "#foo_set-group select.admin-autocomplete", function () { delete $.ajaxSettings.headers["X-Foo"]; } );
- Destroy the widget and rebuild it using
$.fn.djangoAdminSelect2
to hack my extra parameters next to the other expected GET parameters:
let $select = $("#id_my_field"); $select.select2("destroy").djangoAdminSelect2({ ajax: { data: function (params) { return { term: params.term page: params.page app_label: $select.data("app-label"), model_name: $select.data("model-name"), field_name: $select.data("field-name") foo: "bar" } } } });
None of these solutions look clean to me, and I would definitely like to have the ability to add extra parameters to $.fn.djangoAdminSelect2
properly as follows:
autocomplete.js
:
'use strict'; { const $ = django.jQuery; const init = function($element, options, extraParams) { const settings = $.extend({ ajax: { data: function(params) { let defaultParams = { term: params.term, page: params.page, app_label: $element.data('app-label'), model_name: $element.data('model-name'), field_name: $element.data('field-name') }; return $.extend(defaultParams, extraParams) } } }, options); $element.select2(settings); }; $.fn.djangoAdminSelect2 = function(options, extraParams) { const settings = $.extend({}, options); $.each(this, function(i, element) { const $element = $(element); init($element, settings, extraParams); }); 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)); }
consumer_script.js
:
$select = $("#id_my_field"); $select.select2("destroy").djangoAdminSelect2({}, {foo: "bar"});
Change History (9)
comment:1 by , 3 years ago
comment:2 by , 3 years ago
Cc: | added |
---|---|
Resolution: | → duplicate |
Status: | new → closed |
Thanks for this proposition, however I don't think that JavaScript is the right place to inject extra parameters. I would rather customize ModelAdmin.autocomplete_view()
and AutocompleteJsonView
.
Closing as a duplicate of #29700 as documenting AutocompleteJsonView
and ModelAdmin.autocomplete_view()
should fix this issue.
comment:3 by , 3 years ago
Thanks for both answers. However, I respectfully disagree with both of them since both solutions are implemented back-end side and hence lack flexibility for some scenario (including the ones I was opening this ticket for).
Simplest example: I want to dynamically add the value of another field of the form as an extra GET parameter to the autocomplete, to implement some sort of fields-coupling.
comment:4 by , 3 years ago
Resolution: | duplicate → wontfix |
---|---|
Type: | Cleanup/optimization → New feature |
Thanks for both answers. However, I respectfully disagree with both of them since both solutions are implemented back-end side and hence lack flexibility for some scenario (including the ones I was opening this ticket for).
Simplest example: I want to dynamically add the value of another field of the form as an extra GET parameter to the autocomplete, to implement some sort of fields-coupling.
Agreed, there is a use case that is not covered by the current implementation, however the Django Admin is not a universal tool for building an app. You can always override autocomplete.js
in your project.
comment:5 by , 3 years ago
Hi there,
The author of both the autocomplete integration as well as the django-select2 package here.
So, I understand you point and frustration. However, the second solution you are proposing actually looks a lot cleaner to me, than to arbitrarily add a second options object.
Just of consistency I would keep the options we pass to djangoAdminSelect2
the same as the once you'd pass to select2
. It would make it very confusing for many folk if we break with the select2
API.
All that aside, this is not a public API. Therefore, your best bet is to overwrite the whole file, if you need some special implementation or use django-select2
or your special needs.
Please, if you want to change the options object structure, I must kindly ask you to try your luck with the Select2 project first. We simply don't have the resources to maintain a custom implementation.
Best,
Joe
comment:6 by , 3 years ago
Thanks for clarifying the situation Johannes. I'll stick with my own override then :-)
comment:7 by , 3 years ago
Dear Johannes,
I found the following code to both add the requested feature (being able to add extra GET params to the AJAX request) and preserve Select2 API.
I also took the liberty of passing the true
parameter to the $.extend
call that merges default Django settings with user-defined settings in the init
function (line 11), since omitting it caused a bug where other parameters (passed through options
) nested under the ajax
key were overwritten to undefined
(see relevant documentation here: https://api.jquery.com/jQuery.extend/#jQuery-extend-deep-target-object1-objectN).
autocomplete.js
:
'use strict'; { const $ = django.jQuery; const init = function($element, options) { let userData = function (params) {}; if (options.ajax && options.ajax.data) { userData = options.ajax.data; delete options.ajax.data; } const settings = $.extend(true, { ajax: { data: function(params) { const defaultData = { term: params.term, page: params.page, app_label: $element.data('app-label'), model_name: $element.data('model-name'), field_name: $element.data('field-name') }; return $.extend(defaultData, userData(params)) } } }, options); $element.select2(settings); }; $.fn.djangoAdminSelect2 = function(options) { const settings = $.extend({}, options); $.each(this, function(i, element) { const $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)); }
consumer_script.js
:
$select = $("#id_my_field"); $select.select2("destroy").djangoAdminSelect2({ ajax: { data: function (params) { return {foo: "bar"} } // Adding other AJAX settings is now also possible, such as processResults for example processResults: function (data) { console.log("Now possible") return data } } });
Do you think this approach would be compatible with your flow?
Regards
comment:8 by , 3 years ago
Resolution: | wontfix |
---|---|
Status: | closed → new |
For the record, I submitted a PR which was rejected since the flow seems to be that an agreement should be reached here on Trac before moving on to a PR. So could you please take a look either at the code here or at the PR?
Link to the PR: https://github.com/django/django/pull/14305
comment:9 by , 3 years ago
Resolution: | → wontfix |
---|---|
Status: | new → closed |
There is another solution direction mentioned here:
https://stackoverflow.com/questions/55344987/how-to-filter-modeladmin-autocomplete-fields-results-with-the-context-of-limit-c/55476825#55476825