question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

Circular import when importing edit handlers on custom User model file

See original GitHub issue

I have an application using a custom user model, and I want to provide an alternative admin with the ModelAdmin. However,

Steps to Reproduce

  1. Create any application using a custom user model.
  2. Try to import FieldPanel on the same file as the custom user model.
  3. An ImportError will be raised as the server starts. FieldPanel can’t be imported on that file because admin/models.py imports the edit handlers, which import all of the forms from Wagtail Admin, which import Django’s AuthenticationForm and PasswordResetForm from django/contrib/auth/forms.py, which in gets the user model, so there’s some sort of circular import.

Full trace:

Unhandled exception in thread started by <function check_errors.<locals>.wrapper at 0x7fd0f41be1e0>
Traceback (most recent call last):
  File "virtualenv_route/lib/python3.6/site-packages/django/utils/autoreload.py", line 228, in wrapper
    fn(*args, **kwargs)
  File "virtualenv_route/lib/python3.6/site-packages/django/core/management/commands/runserver.py", line 116, in inner_run
    autoreload.raise_last_exception()
  File "virtualenv_route/lib/python3.6/site-packages/django/utils/autoreload.py", line 251, in raise_last_exception
    six.reraise(*_exception)
  File "virtualenv_route/lib/python3.6/site-packages/django/utils/six.py", line 685, in reraise
    raise value.with_traceback(tb)
  File "virtualenv_route/lib/python3.6/site-packages/django/utils/autoreload.py", line 228, in wrapper
    fn(*args, **kwargs)
  File "virtualenv_route/lib/python3.6/site-packages/django/__init__.py", line 27, in setup
    apps.populate(settings.INSTALLED_APPS)
  File "virtualenv_route/lib/python3.6/site-packages/django/apps/registry.py", line 108, in populate
    app_config.import_models()
  File "virtualenv_route/lib/python3.6/site-packages/django/apps/config.py", line 202, in import_models
    self.models_module = import_module(models_module_name)
  File "virtualenv_route/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 994, in _gcd_import
  File "<frozen importlib._bootstrap>", line 971, in _find_and_load
  File "<frozen importlib._bootstrap>", line 955, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 665, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 678, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "virtualenv_route/lib/python3.6/site-packages/wagtail/admin/models.py", line 5, in <module>
    from wagtail.admin import edit_handlers  # NOQA
  File "virtualenv_route/lib/python3.6/site-packages/wagtail/admin/edit_handlers.py", line 24, in <module>
    from .forms import (  # NOQA
  File "virtualenv_route/lib/python3.6/site-packages/wagtail/admin/forms.py", line 5, in <module>
    from django.contrib.auth.forms import AuthenticationForm, PasswordResetForm
  File "virtualenv_route/lib/python3.6/site-packages/django/contrib/auth/forms.py", line 22, in <module>
    UserModel = get_user_model()
  File "virtualenv_route/lib/python3.6/site-packages/django/contrib/auth/__init__.py", line 194, in get_user_model
    return django_apps.get_model(settings.AUTH_USER_MODEL, require_ready=False)
  File "virtualenv_route/lib/python3.6/site-packages/django/apps/registry.py", line 203, in get_model
    app_config.import_models()
  File "virtualenv_route/lib/python3.6/site-packages/django/apps/config.py", line 202, in import_models
    self.models_module = import_module(models_module_name)
  File "virtualenv_route/lib/python3.6/importlib/__init__.py", line 126, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "myapp/web/app/accounts/models.py", line 6, in <module>
    from wagtail.admin.edit_handlers import FieldPanel, FieldRowPanel 
ImportError: cannot import name 'FieldPanel'

Workaround

In this case I solved it by specifying panels directly in get_edit_handler method of a custom UserEditView and specifying edit_view_class on the custom UserAdmin.

Technical details

Wagtail 2.0.1

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Reactions:3
  • Comments:8

github_iconTop GitHub Comments

2reactions
gasmancommented, Sep 12, 2018

Have made an attempt to fix this, by splitting up wagtail.admin.forms into submodules and only importing the relevant submodule where possible (so that wagtail.admin.edit_handlers can import the model-form-hacking stuff without also importing the auth forms): https://github.com/gasman/wagtail/tree/admin-forms-circular-dependency

I’m not totally happy with the result, though: I was hoping we could keep it fully backwards-compatible (i.e. allow all existing from wagtail.admin.forms import ... lines to continue working) by retaining all the imports in wagtail/admin/forms/__init__.py, but this file will also get imported on from wagtail.admin.forms.models import ..., so the circular reference still remains 😦. The only way forward I can see (aside from horrible things like putting the new submodules in a new namespace like wagtail.admin.forms2) is to remove the import of the wagtail.admin.forms.auth forms from wagtail.admin.forms.

Technically, the only documented things in wagtail.admin.forms (and thus the only things we’re duty-bound to keep in their current locations) are WagtailAdminModelForm and WagtailAdminPageForm. In practice, I expect that a lot of external code is importing SearchForm (because they’re copying what wagtailimages / wagtaildocs / contrib apps do) and I wouldn’t be surprised to find that there are third-party apps subclassing LoginForm to do custom authentication (2FA or whatever).

So, anyone know any clever tricks for defining a bunch of things in wagtail.admin.forms without the whole lot getting imported during from wagtail.admin.forms.models import FooForm?

1reaction
gasmancommented, May 4, 2018

Thanks for the report @rafaponieman! Please can you provide an example models.py file that reproduces this issue, starting from a new Wagtail project?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Simple Workaround to Avoid Django Circular Import Errors
In some cases, application models need to reference each other dependently—this can lead to a circular import error. Fortunately, Django has an ...
Read more >
ImportError: cannot import name '...' from partially initialized ...
A similar error with the message "most likely due to a circular import" would occur. The same contents would work fine if I...
Read more >
Importing settings in a module that contains a logging Handler ...
Importing settings in a module that contains a logging Handler causes circular import. Reported by: donspaulding, Owned by: Claude Paroz <claude@…>.
Read more >
Python Circular Import Problem and Solutions
As python prefers importing from the local current directory first and then from site-packages, it will create a circular import problem.
Read more >
Handling circular dependencies - Cloud - 8.0
To fix the data model in such a case, simply turn ... ... Importing user-defined indicators from a csv file (deprecated feature) ·...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found