#34899: Model Field.choices callable support is not actually lazy
-------------------------------------+-------------------------------------
Reporter: Adam | Owner: nobody
Johnson |
Type: Bug | Status: new
Component: Database | Version: 5.0
layer (models, ORM) |
Severity: Release | Keywords:
blocker |
Triage Stage: | Has patch: 0
Unreviewed |
Needs documentation: 0 | Needs tests: 0
Patch needs improvement: 0 | Easy pickings: 0
UI/UX: 0 |
-------------------------------------+-------------------------------------
#24561 added support for callables to model Field.choices, with the docs
declaring that using a callable “can be particularly handy” when you need
I/O like a database query. Whilst the model Field supports this by only
lazily calling choices, the formfield() method ends up iterating over
choices, leading to the callable being run at import time with a
`ModelField` definition.
Here’s a minimal example ([https://github.com/adamchainz/django-5.0
-choices-laziness source]):
{{{
from django import forms
from django.db import models
ready = False
def animals():
if not ready:
raise RuntimeError("Not ready to load animals")
return [
(1, "Aardvark"),
(2, "Banana"),
]
class User(models.Model):
spirit_animal = models.IntegerField(choices=animals)
class UserForm(forms.ModelForm):
class Meta:
model = User
fields = ["spirit_animal"]
ready = True
}}}
On Django 5.0a1 it fails with:
{{{
$ ./manage.py shell
Traceback (most recent call last):
File "/..././manage.py", line 21, in <module>
main()
...
File "/.../example/models.py", line 21, in <module>
class UserForm(forms.ModelForm):
File "/.../.venv/lib/python3.11/site-packages/django/forms/models.py",
line 309, in __new__
fields = fields_for_model(
^^^^^^^^^^^^^^^^^
File "/.../.venv/lib/python3.11/site-packages/django/forms/models.py",
line 234, in fields_for_model
formfield = f.formfield(**kwargs)
^^^^^^^^^^^^^^^^^^^^^
File "/.../.venv/lib/python3.11/site-
packages/django/db/models/fields/__init__.py", line 2142, in formfield
return super().formfield(
^^^^^^^^^^^^^^^^^^
File "/.../.venv/lib/python3.11/site-
packages/django/db/models/fields/__init__.py", line 1118, in formfield
defaults["choices"] = self.get_choices(include_blank=include_blank)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/.../.venv/lib/python3.11/site-
packages/django/db/models/fields/__init__.py", line 1054, in get_choices
choices = list(self.choices)
^^^^^^^^^^^^^^^^^^
File "/.../.venv/lib/python3.11/site-packages/django/utils/choices.py",
line 17, in __iter__
yield from normalize_choices(self.func())
^^^^^^^^^^^
File "/.../example/models.py", line 9, in animals
raise RuntimeError("Not ready to load animals")
RuntimeError: Not ready to load animals
}}}
I encountered this bug whilst trying to update django-countries to support
Django 5.0 (https://github.com/SmileyChris/django-countries/pull/438). Its
sorted, translated list of countries is exactly what the callable choices
feature is intended for.
--
Ticket URL: <https://code.djangoproject.com/ticket/34899>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.
--
You received this message because you are subscribed to the Google Groups
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/django-updates/0107018b2cfa9d8b-f6a869eb-d6dc-47bb-9b77-094f0d998d34-000000%40eu-central-1.amazonses.com.