2π
To understand whatβs going on, consider the nature of the objects at play:
-
ActiveSignalForm
is a class. It is defined within a module, which means that when the module is loaded into the interpreter, the class object is constructed. -
ActiveSignalForm.choices
is a class attribute. As such, it is available to any code that loads theActiveSignalForm
class, before even an instance of that class is created. In other words, you can access it asActiveSignalForm.choices
without first having to create an instance withActiveSignalForm()
. -
In this particular case, the definition involves an assignment β
ActiveSignalForm.choices
will be set to the return value provided by callingforms.MultipleChoiceField()
. -
And finally, two of the calling arguments to
ModelChoiceField
involve comprehension expressions.
With all of this information, you start getting a picture of the chain of events:
-
The module containing the class is loaded. This kicks-off the construction of the
ActiveSignalForm
class. -
The interpreter encounters an assignment expression to define
ActiveSignalForm.choices
. -
The right-hand side of the expression is evaluated, which involves calling the
MultipleChoiceField()
callable. -
In order to do this, the
choices
andinitial
calling arguments must be resolved, and this involves evaluating two comprehension expressions.
So you see, steps 1 through 4 are nested, which results in the behaviour you are seeing β the server is started, the module is loaded, the values are computed once and then never again.
The Django documentation contains this piece of important information about both initial
and choices
fields:
Instead of a constant, you can also pass any callable. The callable will be evaluated only when the unbound form is displayed, not when it is defined.
So it seems that in order to have these fields re-calculated every time a different ActiveSignalForm
instance is created, we will need to define them as callables.
To accomplish this, I believe all you need to do is prefix your comprehension expressions with lambda:
, in order to create a simple callable:
choices = lambda: ((sig.id, sig.signal)
for sig
in registry.objects.order_by('first_registered')),
initial = lambda: [sig.id
for sig
in registry.objects.order_by('first_registered')
if sig.archival_active],
What this does, is it splits step (4), above, into two parts.
4a. Now, when the class is defined, the only thing that needs to be evaluated when resolving the initial=
and choices=
arguments to MultipleChoiceField
is a lambda statement. This creates a tiny function that, when called will evaluate the comprehension expressions within. This allows the nesting to resolve (i.e. step 4a returns a callable for step 3, which returns a result for assignment as a class property in step 2, which then allows the rest of the class definition to proceed in step 1).
4b. (Much) later, when Django is rendering the ActiveSignalForm.choices
field, it encounters the callables we created in (4a) and executes them in order to get the appropriate iterables.
0π
Thereβs no reason to have these list comprehensions. You are filtering on values from querysets, so you should use ModelMultipleChoiceField along with an actual queryset.
choices = forms.ModelMultipleChoiceField(
label='Alter Archiver Registry',
queryset=registry.objects.order_by('first_registered')),
initial=registry.objects.order_by('first_registered').filter(archival_active=True)
)
- [Answered ]-Django app with Rails API
- [Answered ]-Dynamically add input fields and save data to Database using django
- [Answered ]-Using reverse (Parental)ManyToManyField in ModelAdmin
- [Answered ]-Newbie β Django Project with Multiple Apps β Unable to render Views