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}}} 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.obj_to_dict(obj, to_field_name) for obj in
context['object_list']
],
'pagination': {'more': context['page_obj'].has_next()},
})
}}}
where {{{obj_to_dict()}}} contains the original object to dictionary
conversion code that would be now easy to override:
{{{#!python
def obj_to_dict(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 obj_to_dict(self, obj, to_field_name):
return super.obj_to_dict(obj, to_field_name) | {'notes':
obj.notes}
}}}
What do you think, is this acceptable? I'm more than happy to provide the
patch.
--
Ticket URL: <https://code.djangoproject.com/ticket/32993>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
Old description:
New description:
class CustomAutocompleteJsonView(AutocompleteJsonView):
construct the results inside {{{JsonResponse}}} constructor to a separate
method. So instead of
there would be
{{{#!python
class CustomAutocompleteJsonView(AutocompleteJsonView):
--
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:1>
* version: 3.2 => dev
* stage: Unreviewed => Accepted
Comment:
Makes sense to me.
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:2>
* type: Cleanup/optimization => New feature
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:3>
* owner: nobody => mrts
* status: new => assigned
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:4>
* owner: mrts => (none)
* status: assigned => new
* has_patch: 0 => 1
Comment:
[https://github.com/django/django/pull/14752/files PR]
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:5>
* owner: (none) => mrts
* status: new => assigned
Comment:
Just a small note for future: you can keep the ticket assigned to yourself
while you're actively maintaining a PR for it.
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:6>
Comment (by mrts):
Thanks for the heads up! Will keep it in mind in the future.
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:7>
Old description:
> Adding data attributes to items in ordinary non-autocomplete foreign key
> construct the results inside {{{JsonResponse}}} constructor to a separate
New description:
class CustomAutocompleteJsonView(AutocompleteJsonView):
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.
--
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:8>
* type: New feature => Cleanup/optimization
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:9>
* needs_tests: 0 => 1
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:10>
* needs_tests: 1 => 0
* stage: Accepted => Ready for checkin
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:11>
* status: assigned => closed
* resolution: => fixed
Comment:
In [changeset:"9b1158a7e0784686bbe5118a88d4804b99fa4fe1" 9b1158a7]:
{{{
#!CommitTicketReference repository=""
revision="9b1158a7e0784686bbe5118a88d4804b99fa4fe1"
Fixed #32993 -- Added AutocompleteJsonView.serialize_result() to allow
customization.
}}}
--
Ticket URL: <https://code.djangoproject.com/ticket/32993#comment:12>