Opened 15 months ago

Last modified 15 months ago

#34807 closed Bug

importing `django.forms` causes circular import error — at Version 1

Reported by: Collin Anderson Owned by: nobody
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 Tim Graham)

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 (1)

comment:1 by Tim Graham, 15 months ago

Description: modified (diff)
Severity: NormalRelease blocker
Triage Stage: UnreviewedAccepted
Note: See TracTickets for help on using tickets.
Back to Top