Reputation: 92
We can define a field in a django form in the following 2 ways:
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=[(u.id, u.username) for u in User.objects.filter(type="TYPE1")])
AND also as
class MyForm(forms.Form):
pass
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myfield'] = forms.ChoiceField(choices=[(u.id, u.username) for u in User.objects.filter(type="TYPE1")])
What is the difference between the two approaches (if any)?
Upvotes: 1
Views: 62
Reputation: 309089
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=[(u.id, u.username) for u in User.objects.filter(type="TYPE1")])
This defines the field myfield
and fetches the choices at the time that the class is defined. This isn't ideal, because it means you have an SQL query to fetch the choices from the database when the application loads. This will slow down the loading of the application, and the choices will get out of date as you add/remove users.
class MyForm(forms.Form):
pass
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myfield'] = forms.ChoiceField(choices=[(u.id, u.username) for u in User.objects.filter(type="TYPE1")])
This defines the field and the choices when the form is instantiated (e.g. in your view). This means that the choices will be up to date when you display the form.
Another option, and one that I recommend, is to define the field as usual, but set the choices in the __init__
method.
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=())
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myfield'].choices = [(u.id, u.username) for u in User.objects.filter(type="TYPE1")]
You may also find ModelChoiceField
useful.
class MyForm(forms.Form):
myfield = forms.ModelChoiceField(queryset=User.objects.all())
Upvotes: 1
Reputation: 77912
There's a very huge difference...
In the first case, your choices
will be eval'd when the class statement is executed, IOW when the module is imported, and will stay the same for the process lifetime. Since Django is usually deployed using long running processes, this means you will have stalled data, and also if you're deploying in a multi-process mode (which is often the case) you may have different sets of users for differents processes, hence inconsistant values from one request to another (eventually from the get to the post...).
To add to the fun, this kind of bug can be hard to debug since the dev server is running a single process and very often restarted...
In the second case, the choices
list is created afresh each time the form is instanciated, so it's safe - and is of the course the right way to set dynamic choices (at least until 1.7.x included - IIRC 1.8 allow to pass a callable instead of sequence).
As a side note: loading the full models only to retrieve a pair of fields is not really efficient, you'd better use Queryset.values_list()
here. Also, you don't have to (re)instanciate the whole field in the __init__
, you can just declare the field as usual and rebind it's choices
attribute later:
class MyForm(forms.Form):
myfield = forms.ChoiceField(choices=())
def __init__(self, *args, **kwargs):
super(MyForm, self).__init__(*args, **kwargs)
self.fields['myfield'].choices = User.objects.filter(type="TYPE1").values_list("id", "username")
And finally, you may want to have a look at the ModelChoiceField
And finally
Upvotes: 2