Opened 3 years ago

Closed 3 years ago

Last modified 3 years ago

#32659 closed Bug (needsinfo)

Autocomplete field: The results could not be loaded.

Reported by: honyczek Owned by: nobody
Component: contrib.admin Version: 3.2
Severity: Normal Keywords:
Cc: Johannes Maron, Jonathan Willitts Triage Stage: Unreviewed
Has patch: no Needs documentation: no
Needs tests: no Patch needs improvement: no
Easy pickings: no UI/UX: no

Description

I use autocomplete field and which with Django 3.1 worked well, after updating to 3.2 it returns only a message: The results could not be loaded. If I look into browser console, I see a try to open url autocomplete/ with status 403 Forbidden. I thought it is a problem with application permissions, but after I've given superuser permission to the user, it is the same.

Change History (15)

comment:1 by Mariusz Felisiak, 3 years ago

Resolution: needsinfo
Status: newclosed

Thanks for this report, however autocomplete works for me. I don't think you've explained the issue in enough detail to confirm a bug in Django (see e.g. #32619). Please reopen the ticket if you can debug your issue and provide details about why and where Django is at fault.

comment:2 by Steven Mapes, 3 years ago

I've encountered this myself tonight in one of my projects. For me the problem was caching of static files. I use Django Storages with S3 and Cloudfront and even after invalidating files on AWS and re-running collect static I get the HTTP403. If I disable django-storages it "fixes" it. As the initiator is jQuery it's likely the op has a similar issue where a collectstatic is not pushed update files and so an older version of jQuery is being loaded that it not making the correct request.

Django wise the issue is results in django.contrib.admin.views.autocomplete.AutocompleteJsonView.process_request having an empty request.GET QueryDict

Last edited 3 years ago by Steven Mapes (previous) (diff)

comment:3 by Nicolas Meisberger, 3 years ago

I was running into the exact same issue after updating from 3.1 to 3.2.
Basically when filling in data into the select2 inputs, the query made was incomplete. Only the term param was set. The caused the process_request method in django.contrib.admin.views.autocomplete to raise an PermissionDenied exception. Here is the part that i mean:

        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

Maybe there should be a different exception raised here for clarity.

To fix the issue I just ran the collectstatic command and the problems were gone. So most likely this is caused by an old and incompatible js library still in the static files.

in reply to:  3 comment:4 by honyczek, 3 years ago

Replying to Nicolas Meisberger:

To fix the issue I just ran the collectstatic command and the problems were gone. So most likely this is caused by an old and incompatible js library still in the static files.

Thanks for the tip, which resolved me the issue. So definitely, it is not a bug.

comment:5 by Ihab Zeedia, 3 years ago

I ran collectstatic but the issue persists. below the error logs:

django_1    | Forbidden (Permission denied): /admin/autocomplete/
django_1    | Traceback (most recent call last):
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/utils/datastructures.py", line 76, in __getitem__
django_1    |     list_ = super().__getitem__(key)
django_1    | KeyError: 'app_label'
django_1    | 
django_1    | During handling of the above exception, another exception occurred:
django_1    | 
django_1    | Traceback (most recent call last):
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/views/autocomplete.py", line 61, in process_request
django_1    |     app_label = request.GET['app_label']
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/utils/datastructures.py", line 78, in __getitem__
django_1    |     raise MultiValueDictKeyError(key)
django_1    | django.utils.datastructures.MultiValueDictKeyError: 'app_label'
django_1    | 
django_1    | The above exception was the direct cause of the following exception:
django_1    | 
django_1    | Traceback (most recent call last):
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner
django_1    |     response = get_response(request)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response
django_1    |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 250, in wrapper
django_1    |     return self.admin_view(view, cacheable)(*args, **kwargs)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/utils/decorators.py", line 130, in _wrapped_view
django_1    |     response = view_func(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/views/decorators/cache.py", line 44, in _wrapped_view_func
django_1    |     response = view_func(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 232, in inner
django_1    |     return view(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/sites.py", line 417, in autocomplete_view
django_1    |     return AutocompleteJsonView.as_view(admin_site=self)(request)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view
django_1    |     return self.dispatch(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/views/generic/base.py", line 98, in dispatch
django_1    |     return handler(request, *args, **kwargs)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/views/autocomplete.py", line 20, in get
django_1    |     self.term, self.model_admin, self.source_field, to_field_name = self.process_request(request)
django_1    |   File "/usr/local/lib/python3.8/site-packages/django/contrib/admin/views/autocomplete.py", line 65, in process_request
django_1    |     raise PermissionDenied from e
django_1    | django.core.exceptions.PermissionDenied
django_1    | [03/May/2021 22:17:04] "GET /admin/autocomplete/ HTTP/1.1" 403 135

comment:6 by Kegan Gan, 3 years ago

I have the same problem. I ran collectstatic but it still did not work. And it turns out, the web browser is still caching the JavaScript.

Try it with the your web browser on incognito mode, or clear your cache. It should solve the issue.

comment:8 by Ihab Zeedia, 3 years ago

Quick Update:

I noticed that static files not really getting updated after running collectstatic. And it turned out that I need to remove/comment STATICFILES_DIRS from my settings.py then run collectstatic which really updated the files and the above issue was fixed.

comment:9 by Erik van Widenfelt, 3 years ago

This had nothing to do with caching or collectstatic for me. I traced it to the admin_site attribute of AutocompleteJsonView.The view tries to get the model_admin class for the remote model from Django's default admin site. If you registered your remote model in a custom admin site a KeyError is raised (line 84). This behavior has changed from 3.1.12 to 3.2.0. See changes in commit 3071660acfbdf4b5c59457c8e9dc345d5e8894c5.

A quick/dirty fix is to scan through the registered AdminSites in all_sites for the admin_site that has registered the remote_model:

# django/contrib/admin/views/autocomplete.py

from django.contrib.admin.sites import all_sites

class AutocompleteJsonView(BaseListView):
    ....

    def process_request(self, request):
        
        ....
        
       # find the correct admin site for this remote model
        try:
            admin_site = [s for s in all_sites if s.is_registered(remote_model)][0]
        except IndexError as e:
            raise PermissionDenied from e

        # continue as before and get the model admin class from the admin site
        try:
            model_admin = admin_site._registry[remote_model]
        except KeyError as e:
            raise PermissionDenied from e

        # Validate suitability of objects.
        ....

Note that you cannot just register the remote_model with the default admin site. The remote_model needs to be registered to the same admin class as the source_model.

Last edited 3 years ago by Erik van Widenfelt (previous) (diff)

comment:10 by Erik van Widenfelt, 3 years ago

Resolution: needsinfo
Status: closednew

comment:11 by Mariusz Felisiak, 3 years ago

Cc: Johannes Maron added
Resolution: needsinfo
Status: newclosed

Erik, thanks for extra details, however I still cannot reproduce this issue.

The view tries to get the model_admin class for the remote_model from Django's default admin site.

As far as I'm aware that's not true, the view uses an admin site in which it's registered.

Can you provide a sample project?

comment:12 by Jonathan Willitts, 3 years ago

Cc: Jonathan Willitts added

comment:13 by Erik van Widenfelt, 3 years ago

ok, you are correct. This is not a bug.

I had this issue because I was incorrectly declaring url paths in the urls.py for my project. The issue is not with sites.py nor autocomplete.py.

The way I setup the admin URLs for all the admin sites in the project risked non-unique urls; namely, for autocomplete.

# WRONG
path("admin/", app_one_admin.urls), # reverses to admin/autocomplete will always hit this AdminSite !!
path("admin/", app_two_admin.urls), # reverses to admin/autocomplete
path("admin/", app_three_admin.urls), # reverses to admin/autocomplete
path("admin/", admin.site.urls), # reverses to admin/autocomplete

The better way is:

# better
path("app_one_admin/", app_one_admin.urls), # reverses to app_one_admin/autocomplete
path("app_two_admin/", app_two_admin.urls), # reverses to app_two_admin/autocomplete
path("app_three_admin/", app_three_admin.urls), # reverses to app_three_admin/autocomplete
path("admin/", admin.site.urls), # reverses to admin/autocomplete

See docs https://docs.djangoproject.com/en/3.2/ref/contrib/admin/#multiple-admin-sites-in-the-same-urlconf

in reply to:  9 comment:14 by Jameson Parker, 3 years ago

I've run into this exact issue on my project. I'm assuming there is a conflict between the Django Autocomplete Light dependency and Django 3.2 autocomplete_fields.

I'm looking at your example fix below, but I'm not sure where you are implementing this in your Django Project?

Replying to Erik van Widenfelt:

This had nothing to do with caching or collectstatic for me. I traced it to the admin_site attribute of AutocompleteJsonView.The view tries to get the model_admin class for the remote model from Django's default admin site. If you registered your remote model in a custom admin site a KeyError is raised (line 84). This behavior has changed from 3.1.12 to 3.2.0. See changes in commit 3071660acfbdf4b5c59457c8e9dc345d5e8894c5.

A quick/dirty fix is to scan through the registered AdminSites in all_sites for the admin_site that has registered the remote_model:

# django/contrib/admin/views/autocomplete.py

from django.contrib.admin.sites import all_sites

class AutocompleteJsonView(BaseListView):
    ....

    def process_request(self, request):
        
        ....
        
       # find the correct admin site for this remote model
        try:
            admin_site = [s for s in all_sites if s.is_registered(remote_model)][0]
        except IndexError as e:
            raise PermissionDenied from e

        # continue as before and get the model admin class from the admin site
        try:
            model_admin = admin_site._registry[remote_model]
        except KeyError as e:
            raise PermissionDenied from e

        # Validate suitability of objects.
        ....

Note that you cannot just register the remote_model with the default admin site. The remote_model needs to be registered to the same admin class as the source_model.

comment:15 by ml, 3 years ago

The reason for this behavior is a change in the Javascript code in the file contrib/admin/static/admin/js/autocomplete.js from version 3.1 to 3.2 (see below).

In addition to running ./manage.py collectstatic it is also necessary to refresh the browser cache. That can be done by going to the address <site>/static/admin/js/autocomplete.js and clicking Reload.

autocomplete.js in v3.1:

'use strict';
{
    const $ = django.jQuery;
    const init = function($element, options) {
        const settings = $.extend({
            ajax: {
                data: function(params) {
                    return {
                        term: params.term,
                        page: params.page
                    };
                }
            }
        }, options);
        $element.select2(settings);
    };
...

autocomplete.js in v3.2:

'use strict';
{
    const $ = django.jQuery;
    const init = function($element, options) {
        const settings = $.extend({
            ajax: {
                data: function(params) {
                    return {
                        term: params.term,
                        page: params.page,
                        app_label: $element.data('app-label'),
                        model_name: $element.data('model-name'),
                        field_name: $element.data('field-name')
                    };
                }
            }
        }, options);
        $element.select2(settings);
    };
...
Last edited 3 years ago by ml (previous) (diff)
Note: See TracTickets for help on using tickets.
Back to Top