handle autocompletewidget s ajax requests for data paginateby 20 adminsite none def getself request args kwargs self term self modeladmin self sourcefield tofieldname self processrequestrequest if not self haspermrequest raise permissiondenied self objectlist self getqueryset context self getcontextdata return jsonresponse results self serializeresultobj tofieldname for obj in contextobjectlist pagination more contextpageobj hasnext def serializeresultself obj tofieldname return id strgetattrobj tofieldname text strobj def getpaginatorself args kwargs return queryset based on modeladmin getsearchresults qs self modeladmin getquerysetself request qs qs complexfilterself sourcefield getlimitchoicesto qs searchusedistinct self modeladmin getsearchresults self request qs self term if searchusedistinct qs qs distinct return qs def processrequestself request term request get getterm try applabel request getapplabel modelname request getmodelname fieldname request getfieldname except keyerror as e raise permissiondenied from e retrieve objects from parameters try sourcemodel apps getmodelapplabel modelname except lookuperror as e raise permissiondenied from e try sourcefield sourcemodel meta getfieldfieldname except fielddoesnotexist as e raise permissiondenied from e try remotemodel sourcefield remotefield model except attributeerror as e raise permissiondenied from e try modeladmin self adminsite getmodeladminremotemodel except notregistered as e raise permissiondenied from e validate suitability of objects if not modeladmin getsearchfieldsrequest raise http404 s must have searchfields for the autocompleteview typemodeladmin qualname tofieldname getattr sourcefield remotefield fieldname remotemodel meta pk attname tofieldname remotemodel meta getfieldtofieldname attname if not modeladmin tofieldallowedrequest tofieldname raise permissiondenied return term modeladmin sourcefield tofieldname def haspermself request objnone handle autocompletewidget s ajax requests for data return a jsonresponse with search results as defined in serialize_result by default results id 123 text foo pagination more true convert the provided model object to a dictionary that is added to the results list use the modeladmin s paginator return queryset based on modeladmin get_search_results 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 retrieve objects from parameters validate suitability of objects check if user has permission to access the related model
from django.apps import apps
from django.contrib.admin.exceptions import NotRegistered
from django.core.exceptions import FieldDoesNotExist, PermissionDenied
from django.http import Http404, JsonResponse
from django.views.generic.list import BaseListView


class AutocompleteJsonView(BaseListView):
    

    paginate_by = 20
    admin_site = None

    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": [
                    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):
        
        return {"id": str(getattr(obj, to_field_name)), "text": str(obj)}

    def get_paginator(self, *args, **kwargs):
        
        return self.model_admin.get_paginator(self.request, *args, **kwargs)

    def get_queryset(self):
        
        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):
        
        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

        
        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.get_model_admin(remote_model)
        except NotRegistered as e:
            raise PermissionDenied from e

        
        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):
        
        return self.model_admin.has_view_permission(request, obj=obj)