If you need to override an autocomplete's behaviour based on its use in a django form, it is how I managed to do it.
Normally you would use formfield_for_foreignkey but when using autocomplete_fields
is not taken in account by django admin.
Note: this is not dynamic based based on the values in the form
This file is a modified version of django/contrib/admin/static/admin/js/autocomplete.js
It overrides the existing function of existing file.
my_app/static/my_app/admin/js/autocomplete_filtered.js
'use strict';
{
const $ = django.jQuery;
$.fn.djangoAdminSelect2 = function() {
$.each(this, function(i, element) {
$(element).select2({
ajax: {
data: (params) => {
return {
term: params.term,
page: params.page,
app_label: element.dataset.appLabel,
model_name: element.dataset.modelName,
field_name: element.dataset.fieldName,
params: element.dataset.fieldParams // <-- adds this
};
}
}
});
});
return this;
};
}
my_app/widgets.py
from urllib.parse import urlencode
from django.contrib.admin import widgets
from django import forms
class FilteredAutoCompleteSelect(widgets.AutocompleteSelect):
def __init__(self, field, admin_site, params: dict, attrs=None, using=None):
super().__init__(field, admin_site, attrs, using)
self.params = params
def build_attrs(self, base_attrs, extra_attrs=None):
attrs = super().build_attrs(base_attrs, extra_attrs)
attrs["data-field-params"] = urlencode(self.params)
return attrs
@property
def media(self):
return super().media + forms.Media(
js=(
"admin/js/jquery.init.js",
"my_app/admin/js/autocomplete_filtered.js",
)
)
from django.db import models
class Parent(models.Model):
pass
class Child(models.Model):
parent = models.ForeignKey(Parent, on_delete=django.db.models.deletion.CASCADE,)
name = models.Charfield(max_length=100)
from django import forms
from django.contrib import admin
from .widgets import FilteredAutoCompleteSelect
from .models import Child
class ChildForm(forms.ModelForm):
class Meta:
model = Child
widgets = {
"crop": FilteredAutoCompleteSelect(
Child.parent.field,
admin.site,
params={"foo": "1"},
)
}
fields = "__all__"
from django.contrib import admin
from .models import Parent, Child
from .forms import ChildForm
@admin.register(Child)
class ChildAdmin(admin.ModelAdmin):
model = Child
form = ChildForm
@admin.register(Parent)
class ParentAdmin(admin.ModelAdmin):
search_fields = ("name", )
model = Parent
def get_search_results(self, request, queryset, search_term):
queryset, use_distinct = super().get_search_results(
request, queryset, search_term
)
params = request.GET.get("params")
if params:
q_params = QueryDict(params)
if "foo" in q_params:
# DO SOMETHING WITH QUERYSET HERE
queryset = queryset.filter()
return queryset, use_distinct
The url will look like this in the Network Tab of your browser
http://example.com/admin/autocomplete/?app_label=my_app&model_name=child&field_name=parent¶ms=foo%3D1