Django runserver failing during admin site checks

Viewed 176

When running runserver command, we are getting the error below which makes breaks the runserver command:

Traceback (most recent call last):                                                                                         
  File "/usr/local/lib/python3.7/threading.py", line 926, in _bootstrap_inner                                              
    self.run()                                                                                                             
  File "/usr/local/lib/python3.7/threading.py", line 870, in run                                                           
    self._target(*self._args, **self._kwargs)                                                                              
  File "/usr/local/lib/python3.7/site-packages/django/utils/autoreload.py", line 54, in wrapper                            
    fn(*args, **kwargs)                                                                                                    
  File "/usr/local/lib/python3.7/site-packages/django/core/management/commands/runserver.py", line 117, in inner_run       
    self.check(display_num_errors=True)                                                                                    
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 390, in check                         
    include_deployment_checks=include_deployment_checks,                                                                   
  File "/usr/local/lib/python3.7/site-packages/django/core/management/base.py", line 377, in _run_checks                   
    return checks.run_checks(**kwargs)                                                                                     
  File "/usr/local/lib/python3.7/site-packages/django/core/checks/registry.py", line 72, in run_checks                     
    new_errors = check(app_configs=app_configs)                                                                            
  File "/usr/local/lib/python3.7/site-packages/django/contrib/admin/checks.py", line 55, in check_admin_app                
    for site in all_sites:                                                                                                 
  File "/usr/local/lib/python3.7/_weakrefset.py", line 60, in __iter__                                                     
    for itemref in self.data:                                                                                              
RuntimeError: Set changed size during iteration

This issue started to happen out of nowhere, since all the code related to AdminSite's wasn't modified in months. It's also not happening every time, but increasing in frequency of occurrence.

It doesn't seem to be related to the current implementation, since it's a django's runserver issue during the system checks, but here are the Implementation details:

class PaymentsAdminSite(AdminSite):
    site_header = "Payments Admin Site"


site = PaymentsAdminSite(name="payments-admin")


@admin.register(models.Payment, site=site)
class PaymentsAdmin(admin.ModelAdmin):
    def cancel(self, request, queryset):
       pass

    actions = (cancel,)

    def get_queryset(self, request):
        return models.Payment.objects.all()
# urls.py
url(r"^payments-admin/", site.urls)
1 Answers

Why is the error occurring:

When django runs it loads all of the apps and runs various checks. The default admin interface has various checks. The one that is failing is django.contrib.admin.checks.check_admin_app. It loops through all_sites and performs a check on each of these sites (AdminSites). The reason this error is being raised is that this set all_sites is being edited whilst it is being looped through (obviously a big no no).

How is it being edited? Well... all_sites refers to a WeakSet in admin.sites, and every time you instantiate an AdminSite, it adds itself to this WeakSet:

# django.contrib.admin.sites

all_sites = WeakSet()

class AdminSite:
    def __init__(self, name='admin'):
        ...
        all_sites.add(self)

Why is this happening?

I'm not 100% sure what is causing the inconsistency but I suspect the module is being loaded in a thread separate to the thread running the checks, and depending on which thread runs faster, the error occurs sometimes and not others.

How can I fix it?

Again, without a better idea of what is going on re. threads and what modules are getting imported when, I can't be 100% that this will fix the above, but I'll explain why I think it should work below. Put the following in your apps.py of the relevant app:

class PaymentsAdminSite(AdminSite):
    site_header = "Payments Admin Site"


site = PaymentsAdminSite(name="payments-admin")

Why should it fix it

One of the first things Django does first is to register all of the apps in your INSTALLED_APPS. This involves importing all of the apps.py modules. After it has registered all of the apps, it will then register all of the models, and then it will call the ready method in each of the AppConfigs. It is the ready method in django.contrib.admin that is adding the check above, so hopefully by instantiating your SiteAdmin in an app.py file, it should be instantiated before the check is even added, let alone run.

Note you should only add the AdminSite to your apps.py, all of the registering of models to it, should stay in your admin.py since those models won't even be registered (to django) yet, at the point that apps.py is run.

Related