Opened 2 years ago
Closed 2 years ago
#34807 closed Bug (fixed)
importing `django.forms` causes circular import error
| Reported by: | Collin Anderson | Owned by: | Natalia Bidart |
|---|---|---|---|
| Component: | Forms | Version: | dev |
| Severity: | Release blocker | Keywords: | |
| Cc: | Nick Pope | Triage Stage: | Ready for checkin |
| Has patch: | yes | Needs documentation: | no |
| Needs tests: | no | Patch needs improvement: | no |
| Easy pickings: | no | UI/UX: | no |
Description (last modified by )
circular import between forms and models and choices.
before [500e01073adda32d514962] you could import forms just fine.
git checkout 500e01073adda32d514962^ python3 -c"from django.db import models" # works fine python3 -c"from django import forms" # works fine
but since [500e01073adda32d514962] this hasn't worked due to circular import.
git checkout 500e01073adda32d514962
python3 -c"from django.db import models" # works fine
python3 -c"from django import forms" # fails:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "django/forms/__init__.py", line 6, in <module>
from django.forms.boundfield import * # NOQA
File "django/forms/boundfield.py", line 5, in <module>
from django.forms.widgets import MultiWidget, Textarea, TextInput
File "django/forms/widgets.py", line 15, in <module>
from django.utils.choices import normalize_choices
File "django/utils/choices.py", line 3, in <module>
from django.db.models.enums import ChoicesMeta
File "django/db/models/__init__.py", line 3, in <module>
from django.db.models.aggregates import * # NOQA
File "django/db/models/aggregates.py", line 5, in <module>
from django.db.models.expressions import Case, Func, Star, Value, When
File "django/db/models/expressions.py", line 12, in <module>
from django.db.models import fields
File "django/db/models/fields/__init__.py", line 18, in <module>
from django.utils.choices import CallableChoiceIterator, normalize_choices
ImportError: cannot import name 'CallableChoiceIterator' from partially initialized module 'django.utils.choices' (most likely due to a circular import) (django/utils/choices.py)
python3 -c"from django.utils import choices" # also fails:
Traceback (most recent call last):
File "<string>", line 1, in <module>
File "django/utils/choices.py", line 3, in <module>
from django.db.models.enums import ChoicesMeta
File "django/db/models/__init__.py", line 3, in <module>
from django.db.models.aggregates import * # NOQA
File "django/db/models/aggregates.py", line 5, in <module>
from django.db.models.expressions import Case, Func, Star, Value, When
File "django/db/models/expressions.py", line 12, in <module>
from django.db.models import fields
File "django/db/models/fields/__init__.py", line 10, in <module>
from django import forms
File "django/forms/__init__.py", line 6, in <module>
from django.forms.boundfield import * # NOQA
File "django/forms/boundfield.py", line 5, in <module>
from django.forms.widgets import MultiWidget, Textarea, TextInput
File "django/forms/widgets.py", line 15, in <module>
from django.utils.choices import normalize_choices
ImportError: cannot import name 'normalize_choices' from partially initialized module 'django.utils.choices' (most likely due to a circular import) (django/utils/choices.py)
Models has always had a dependency on Forms, but now Models depends on Forms depends on utils.choices which depend on models.enums.ChoicesMeta
Summary of the circle: django.forms -> forms.widgets -> django.utils.choices -> django.db.models.enums -> models.__init__ -> models.aggregates -> models.fields -> django.forms.
If django.db.models is imported before forms then it happens to import fine because the order of imports happens to lines up perfectally, but it's very very fragile. models.__init__ -> models.aggregates -> models.fields -> django.forms -> forms.widgets -> django.utils.choices -> django.db.models.enums.
Change History (8)
comment:1 by , 2 years ago
| Description: | modified (diff) |
|---|---|
| Severity: | Normal → Release blocker |
| Triage Stage: | Unreviewed → Accepted |
comment:2 by , 2 years ago
| Owner: | changed from to |
|---|---|
| Status: | new → assigned |
comment:3 by , 2 years ago
| Cc: | added |
|---|
comment:7 by , 2 years ago
| Triage Stage: | Accepted → Ready for checkin |
|---|
One thing that I don't understand is that when using a Django shell, the import works just fine. Tests also perform multiple
from django import formsand they do not fail. OTOH, I can reproduce with the provided:Does anyone understand why the difference? (my main question is why this wasn't picked up by the tests or my IRL testing within a Django test project)