I ran into a use case today that required a model form to have some fields disabled (readonly), but only when a certain condition was met (specifically when a property of the model was set to true) . Since the model form is used with the fields both enabled and disabled, simply duplicating the model form and removing the necessary fields wasn’t an option (plus that’s not exactly DRY now is it).
The solution ended up being quite simple. First, you’ll need to override the clean method for each field that needs to be disabled within your model form class:
# the following fields can only be updated when the is_disabled property is set to false | |
def clean_field_1(self): | |
if self.instance.is_disabled: | |
return self.instance.field_1 | |
else: | |
return self.cleaned_data.get('field_1') | |
def clean_field_2(self): | |
if self.instance.is_disabled: | |
return self.instance.field_2 | |
else: | |
return self.cleaned_data.get('field_2') |
The clean field method simply checks the is_disabled property and if it’s true, returns the existing field data. It only returns the new submitted data, pulled from cleaned_data, if is_disabled is false.
The second part of the solution is to set the field to readonly in your template. Note that you need to use readonly rather than disabled as the field won’t actually be submitted if set to disabled (more info on this here). This will cause Django to throw a missing field error before the clean field method ever runs.
You can add the necessary html to the form field via the widget’s attributes property:
modelform.fields['field_1'].widget.attrs['readonly'] = True |