﻿id	summary	reporter	owner	description	type	status	component	version	severity	resolution	keywords	cc	stage	has_patch	needs_docs	needs_tests	needs_better_patch	easy	ui_ux
32993	Refactor AutocompleteJsonView to support extra fields in autocomplete response	mrts	mrts	"Adding data attributes to items in ordinary non-autocomplete foreign key fields that use {{{forms.widgets.Select}}}-based widgets is relatively easy. This enables powerful and dynamic admin site customizations where fields from related models are updated immediately when users change the selected item.

However, adding new attributes to autocomplete field results currently requires extending {{{contrib.admin.views.autocomplete.AutocompleteJsonView}}} and fully overriding the {{{AutocompleteJsonView.get()}}} method. Here's an example:

{{{#!python
class MyModelAdmin(admin.ModelAdmin):
    def get_urls(self):
        return [
            path('autocomplete/', CustomAutocompleteJsonView.as_view(admin_site=self.admin_site))
            if url.pattern.match('autocomplete/')
            else url for url in super().get_urls()
        ]

class CustomAutocompleteJsonView(AutocompleteJsonView):

    def get(self, request, *args, **kwargs):
        self.term, self.model_admin, self.source_field, to_field_name = self.process_request(request)

        if not self.has_perm(request):
            raise PermissionDenied

        self.object_list = self.get_queryset()
        context = self.get_context_data()
        return JsonResponse({
            'results': [
                {'id': str(getattr(obj, to_field_name)), 'text': str(obj), 'notes': obj.notes} # <-- customization here
                for obj in context['object_list']
            ],
            'pagination': {'more': context['page_obj'].has_next()},
        })
}}}


The problem with this is that as {{{AutocompleteJsonView.get()}}} keeps evolving, there's quite a lot of maintenance overhead required to catch up.

The solutions is simple, side-effect- and risk-free: adding a result customization extension point to {{{get()}}} by moving the lines that construct the results inside {{{JsonResponse}}} constructor to a separate method. So instead of

{{{#!python
        return JsonResponse({
            'results': [
                {'id': str(getattr(obj, to_field_name)), 'text': str(obj)}
                for obj in context['object_list']
            ],
            'pagination': {'more': context['page_obj'].has_next()},
        })
}}}

there would be

{{{#!python
        return JsonResponse({
            'results': [
                self.serialize_result(obj, to_field_name) for obj in context['object_list']
            ],
            'pagination': {'more': context['page_obj'].has_next()},
        })
}}}

where {{{serialize_result()}}} contains the original object to dictionary conversion code that would be now easy to override:

{{{#!python
def serialize_result(self, obj, to_field_name):
    return {'id': str(getattr(obj, to_field_name)), 'text': str(obj)}
}}}

The example {{{CustomAutocompleteJsonView}}} from above would now become succinct and maintainable:

{{{#!python
class CustomAutocompleteJsonView(AutocompleteJsonView):

    def serialize_result(self, obj, to_field_name):
        return super.serialize_result(obj, to_field_name) | {'notes': obj.notes}
}}}

What do you think, is this acceptable? I'm more than happy to provide the patch."	Cleanup/optimization	closed	contrib.admin	dev	Normal	fixed			Ready for checkin	1	0	0	0	1	0
