Make your ModelForms aware of whether they're part of a CreateView or an UpdateView
Even though they're usually part of a View
, Django's Form
s 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 Widget
s), 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 ModelForm
s, which holds an instance of your model class (i.e. Appointment
). In the context of a CreateView
, instance
isn't saved to the DB yet, therefore its pk
is None
. Simple as that!