У меня есть ModelAdmin в Django 2.1.3 вот так:
class BoxAdmin(admin.ModelAdmin):
autocomplete_fields = ['testkit']
def formfield_for_foreignkey(self, db_field, request, **kwargs):
if db_field.name == 'testkit':
kwargs['queryset'] = Barcode.objects.exclude(testkit__in=Box.objects.all().values('testkit'))
return super().formfield_for_foreignkey(db_field, request, **kwargs)
метод formfield_for_foreignkey написан для "традиционных" полей внешнего ключа. Для поля автозаполнения он обеспечивает отображение ошибки при выборе тестового набора вне набора запросов. Однако это не ограничивает результаты, найденные в поле автозаполнения. В документация не упоминаются какие-либо ограничения для пользовательских наборов запросов. Этот ответ имеет отношение, но имеет дело только с авторизацией.
Можно переопределить метод get_search_result соответствующего ModelAdmin.
class TestkitAdmin(admin.ModelAdmin):
search_fields = ['number']
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(request, queryset, search_term)
if 'autocomplete' in request.path:
queryset = queryset.exclude(testkit__in=Box.objects.all().values('testkit'))
return queryset, use_distinct
Но я не могу определить, из какого поля автозаполнения пришел поисковый запрос. Таким образом, я могу запрограммировать его только так, чтобы он отвечал потребностям одного ссылающегося на ModelAdmin.
Как правильно ограничить набор запросов для поля автозаполнения?






Вы можете переопределить статический файл Django autocomplete.js в static/admin/js/autocomplete.js. Django всегда будет отдавать предпочтение перезаписанному вами файлу.
Затем в перезаписанном файле измените функцию djangoAdminSelect2() примерно так:
$.fn.djangoAdminSelect2 = function(options) {
var settings = $.extend({}, options);
$.each(this, function(i, element) {
var $element = $(element);
$.extend(settings, {
'ac_field_name': $element.parents().filter('.related-widget-wrapper').find('select').attr('name')
});
init($element, settings);
});
return this;
};
а функция init() выглядит так:
var init = function($element, options) {
var settings = $.extend({
ajax: {
data: function(params) {
return {
term: params.term,
page: params.page,
field_name: options.ac_field_name
};
}
}
}, options);
$element.select2(settings);
};
Затем в вашем get_search_results() просто получите доступ к параметру GET запроса field_name и вуаля.
Конечно, можно сделать так, чтобы JS выглядел лучше, но в принципе это работает так.
Я использую это в модели, которая имеет несколько ForeignKey для одной и той же модели, где мне нужны разные варианты автозаполнения для каждого ForeignKey. Я не разбираюсь в javascript, но выяснил, что ac_field_name заполняется при загрузке страницы и содержит только одно значение, а не одно для каждого поля. Поэтому я вернулся к исходному autocomplete.js и просто добавил
field_name: $element.attr('name')в функцию init return dict (не забудьте добавить запятую в предыдущей строке!). Я переопределил autocomplete.js для всего сайта, так как не смог загрузить js-класс Media в ModelAdmin после django-js.