123 lines
4.2 KiB
Python
123 lines
4.2 KiB
Python
from django.apps import apps
|
|
from django.core.exceptions import FieldDoesNotExist, PermissionDenied
|
|
from django.http import Http404, JsonResponse
|
|
from django.views.generic.list import BaseListView
|
|
|
|
|
|
class AutocompleteJsonView(BaseListView):
|
|
"""Handle AutocompleteWidget's AJAX requests for data."""
|
|
|
|
paginate_by = 20
|
|
admin_site = None
|
|
|
|
def get(self, request, *args, **kwargs):
|
|
"""
|
|
Return a JsonResponse with search results as defined in
|
|
serialize_result(), by default:
|
|
{
|
|
results: [{id: "123" text: "foo"}],
|
|
pagination: {more: true}
|
|
}
|
|
"""
|
|
(
|
|
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": [
|
|
self.serialize_result(obj, to_field_name)
|
|
for obj in context["object_list"]
|
|
],
|
|
"pagination": {"more": context["page_obj"].has_next()},
|
|
}
|
|
)
|
|
|
|
def serialize_result(self, obj, to_field_name):
|
|
"""
|
|
Convert the provided model object to a dictionary that is added to the
|
|
results list.
|
|
"""
|
|
return {"id": str(getattr(obj, to_field_name)), "text": str(obj)}
|
|
|
|
def get_paginator(self, *args, **kwargs):
|
|
"""Use the ModelAdmin's paginator."""
|
|
return self.model_admin.get_paginator(self.request, *args, **kwargs)
|
|
|
|
def get_queryset(self):
|
|
"""Return queryset based on ModelAdmin.get_search_results()."""
|
|
qs = self.model_admin.get_queryset(self.request)
|
|
qs = qs.complex_filter(self.source_field.get_limit_choices_to())
|
|
qs, search_use_distinct = self.model_admin.get_search_results(
|
|
self.request, qs, self.term
|
|
)
|
|
if search_use_distinct:
|
|
qs = qs.distinct()
|
|
return qs
|
|
|
|
def process_request(self, request):
|
|
"""
|
|
Validate request integrity, extract and return request parameters.
|
|
|
|
Since the subsequent view permission check requires the target model
|
|
admin, which is determined here, raise PermissionDenied if the
|
|
requested app, model or field are malformed.
|
|
|
|
Raise Http404 if the target model admin is not configured properly with
|
|
search_fields.
|
|
"""
|
|
term = request.GET.get("term", "")
|
|
try:
|
|
app_label = request.GET["app_label"]
|
|
model_name = request.GET["model_name"]
|
|
field_name = request.GET["field_name"]
|
|
except KeyError as e:
|
|
raise PermissionDenied from e
|
|
|
|
# Retrieve objects from parameters.
|
|
try:
|
|
source_model = apps.get_model(app_label, model_name)
|
|
except LookupError as e:
|
|
raise PermissionDenied from e
|
|
|
|
try:
|
|
source_field = source_model._meta.get_field(field_name)
|
|
except FieldDoesNotExist as e:
|
|
raise PermissionDenied from e
|
|
try:
|
|
remote_model = source_field.remote_field.model
|
|
except AttributeError as e:
|
|
raise PermissionDenied from e
|
|
try:
|
|
model_admin = self.admin_site._registry[remote_model]
|
|
except KeyError as e:
|
|
raise PermissionDenied from e
|
|
|
|
# Validate suitability of objects.
|
|
if not model_admin.get_search_fields(request):
|
|
raise Http404(
|
|
"%s must have search_fields for the autocomplete_view."
|
|
% type(model_admin).__qualname__
|
|
)
|
|
|
|
to_field_name = getattr(
|
|
source_field.remote_field, "field_name", remote_model._meta.pk.attname
|
|
)
|
|
to_field_name = remote_model._meta.get_field(to_field_name).attname
|
|
if not model_admin.to_field_allowed(request, to_field_name):
|
|
raise PermissionDenied
|
|
|
|
return term, model_admin, source_field, to_field_name
|
|
|
|
def has_perm(self, request, obj=None):
|
|
"""Check if user has permission to access the related model."""
|
|
return self.model_admin.has_view_permission(request, obj=obj)
|