don't import authtoken model until needed by sheppard · Pull Request #3785 · encode/django-rest-framework (original) (raw)

This PR updates the usage of the authtoken model for better compatibility with Django 1.9, specifically to make it possible to define module-level app APIs that rely on rest_framework with the default authentication settings (which import rest_framework.authentication).

Some background: it is forbidden in Django 1.9 to import models before the app infrastructure has finished loading. This makes it a bit tricky when implementing module-level shortcut APIs like Django's admin.site, wq.db's rest.router interface, and the rest_pandas module (though the latter isn't strictly a Django "app"). Any code imported in the __init__.py for those modules needs to defer importing models until they are needed (c.f. django/django#2228).

Currently, rest_framework.authentication imports the Token model at the top level and therefore cannot be imported in Django 1.9 until after apps are done initializing (see the traceback below). This patch simply defers loading the Token model until it is needed, thus avoiding the import error. As a side effect, this also means that rest_framework.authtoken.models is never imported unless it is being used, which means the workaround for #705 is no longer needed. This issue is similar but not the same - in #705, the error occured when authtoken was not in INSTALLED_APPS, whereas this issue will occur whether or not authtoken is in INSTALLED_APPS.

(test initialization stuff...) File "/local/path/wq.db/tests/init.py", line 10, in django.setup() File "/usr/local/lib/python3.4/dist-packages/django/init.py", line 18, in setup apps.populate(settings.INSTALLED_APPS) File "/usr/local/lib/python3.4/dist-packages/django/apps/registry.py", line 85, in populate app_config = AppConfig.create(entry) File "/usr/local/lib/python3.4/dist-packages/django/apps/config.py", line 90, in create module = import_module(entry) File "/usr/lib/python3.4/importlib/init.py", line 109, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "/local/path/wq.db/wq/db/rest/init.py", line 2, in from .routers import ModelRouter, router # NOQA File "/local/path/wq.db/wq/db/rest/routers.py", line 7, in from rest_framework.routers import DefaultRouter, Route File "/usr/local/lib/python3.4/dist-packages/rest_framework/routers.py", line 25, in from rest_framework import views File "/usr/local/lib/python3.4/dist-packages/rest_framework/views.py", line 98, in class APIView(View): File "/usr/local/lib/python3.4/dist-packages/rest_framework/views.py", line 103, in APIView authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES File "/usr/local/lib/python3.4/dist-packages/rest_framework/settings.py", line 203, in getattr val = perform_import(val, attr) File "/usr/local/lib/python3.4/dist-packages/rest_framework/settings.py", line 148, in perform_import return [import_from_string(item, setting_name) for item in val] File "/usr/local/lib/python3.4/dist-packages/rest_framework/settings.py", line 148, in return [import_from_string(item, setting_name) for item in val] File "/usr/local/lib/python3.4/dist-packages/rest_framework/settings.py", line 160, in import_from_string module = importlib.import_module(module_path) File "/usr/lib/python3.4/importlib/init.py", line 109, in import_module return _bootstrap._gcd_import(name[level:], package, level) File "/usr/local/lib/python3.4/dist-packages/rest_framework/authentication.py", line 13, in from rest_framework.authtoken.models import Token File "/usr/local/lib/python3.4/dist-packages/rest_framework/authtoken/models.py", line 16, in class Token(models.Model): File "/usr/local/lib/python3.4/dist-packages/django/db/models/base.py", line 94, in new app_config = apps.get_containing_app_config(module) File "/usr/local/lib/python3.4/dist-packages/django/apps/registry.py", line 239, in get_containing_app_config self.check_apps_ready() File "/usr/local/lib/python3.4/dist-packages/django/apps/registry.py", line 124, in check_apps_ready raise AppRegistryNotReady("Apps aren't loaded yet.") django.core.exceptions.AppRegistryNotReady: Apps aren't loaded yet.