Even though they're usually part of a
Forms are, by default, not aware of the context in which they're being used. This makes perfect sense, in harmony with the principle of loose coupling: a
Form needs to take care of data validation, as well as rendering (via
Widgets), and nothing else.
It is often the case, however, that I need a
ModelForm to act a little differently whether it's brought up as part of a
CreateView or an
UpdateView. Let's look at a quick example - let's say we're creating a model (say, an
Appointment) that has a
ForeignKey relation to an
User - and we only want to have active users shown in our
ModelChoiceField. However, when you're editing an existing
Appointment that belongs to an inactive user, you obviously need to have that user in the queryset too, or the form won't validate. What do we do?
One way of implementing different behavior for forms is to just use different forms; yet another - to make the form's constructor accept an
is_createview keyword argument, and add that to the
dict that is returned by a CreateView's
get_form_kwargs. Both ways require that you write more code than is necessary, especially when you can simply do:
class AppointmentForm(ModelForm): def __init__(self, *args, **kwargs): if self.instance.pk is None: # it's a CreateView self.fields['user'].queryset = ... # active users else: # it's an UpdateView: self.fields['user'].queryset = ... # active users + self.instance.user
instance is a (very useful) class attribute of
ModelForms, which holds an instance of your model class (i.e.
Appointment). In the context of a
instance isn't saved to the DB yet, therefore its
None. Simple as that!