autocomplete.py 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. from django.apps import apps
  2. from django.core.exceptions import FieldDoesNotExist, PermissionDenied
  3. from django.http import Http404, JsonResponse
  4. from django.views.generic.list import BaseListView
  5. class AutocompleteJsonView(BaseListView):
  6. """Handle AutocompleteWidget's AJAX requests for data."""
  7. paginate_by = 20
  8. admin_site = None
  9. def get(self, request, *args, **kwargs):
  10. """
  11. Return a JsonResponse with search results of the form:
  12. {
  13. results: [{id: "123" text: "foo"}],
  14. pagination: {more: true}
  15. }
  16. """
  17. self.term, self.model_admin, self.source_field, to_field_name = self.process_request(request)
  18. if not self.has_perm(request):
  19. raise PermissionDenied
  20. self.object_list = self.get_queryset()
  21. context = self.get_context_data()
  22. return JsonResponse({
  23. 'results': [
  24. {'id': str(getattr(obj, to_field_name)), 'text': str(obj)}
  25. for obj in context['object_list']
  26. ],
  27. 'pagination': {'more': context['page_obj'].has_next()},
  28. })
  29. def get_paginator(self, *args, **kwargs):
  30. """Use the ModelAdmin's paginator."""
  31. return self.model_admin.get_paginator(self.request, *args, **kwargs)
  32. def get_queryset(self):
  33. """Return queryset based on ModelAdmin.get_search_results()."""
  34. qs = self.model_admin.get_queryset(self.request)
  35. qs = qs.complex_filter(self.source_field.get_limit_choices_to())
  36. qs, search_use_distinct = self.model_admin.get_search_results(self.request, qs, self.term)
  37. if search_use_distinct:
  38. qs = qs.distinct()
  39. return qs
  40. def process_request(self, request):
  41. """
  42. Validate request integrity, extract and return request parameters.
  43. Since the subsequent view permission check requires the target model
  44. admin, which is determined here, raise PermissionDenied if the
  45. requested app, model or field are malformed.
  46. Raise Http404 if the target model admin is not configured properly with
  47. search_fields.
  48. """
  49. term = request.GET.get('term', '')
  50. try:
  51. app_label = request.GET['app_label']
  52. model_name = request.GET['model_name']
  53. field_name = request.GET['field_name']
  54. except KeyError as e:
  55. raise PermissionDenied from e
  56. # Retrieve objects from parameters.
  57. try:
  58. source_model = apps.get_model(app_label, model_name)
  59. except LookupError as e:
  60. raise PermissionDenied from e
  61. try:
  62. source_field = source_model._meta.get_field(field_name)
  63. except FieldDoesNotExist as e:
  64. raise PermissionDenied from e
  65. try:
  66. remote_model = source_field.remote_field.model
  67. except AttributeError as e:
  68. raise PermissionDenied from e
  69. try:
  70. model_admin = self.admin_site._registry[remote_model]
  71. except KeyError as e:
  72. raise PermissionDenied from e
  73. # Validate suitability of objects.
  74. if not model_admin.get_search_fields(request):
  75. raise Http404(
  76. '%s must have search_fields for the autocomplete_view.' %
  77. type(model_admin).__qualname__
  78. )
  79. to_field_name = getattr(source_field.remote_field, 'field_name', remote_model._meta.pk.attname)
  80. to_field_name = remote_model._meta.get_field(to_field_name).attname
  81. if not model_admin.to_field_allowed(request, to_field_name):
  82. raise PermissionDenied
  83. return term, model_admin, source_field, to_field_name
  84. def has_perm(self, request, obj=None):
  85. """Check if user has permission to access the related model."""
  86. return self.model_admin.has_view_permission(request, obj=obj)