#32993: Refactor AutocompleteJsonView to support extra fields in autocomplete
response
------------------------------------------------+------------------------
               Reporter:  mrts                  |          Owner:  nobody
                   Type:  Cleanup/optimization  |         Status:  new
              Component:  contrib.admin         |        Version:  3.2
               Severity:  Normal                |       Keywords:
           Triage Stage:  Unreviewed            |      Has patch:  0
    Needs documentation:  0                     |    Needs tests:  0
Patch needs improvement:  0                     |  Easy pickings:  1
                  UI/UX:  0                     |
------------------------------------------------+------------------------
 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}}} 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.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to django-updates+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/django-updates/047.7c09c39fb38dc02cb78072efa4b993f4%40djangoproject.com.

Reply via email to