Question
In a Django form, how do I make a field read-only (or disabled)?
When the form is being used to create a new entry, all fields should be enabled - but when the record is in update mode some fields need to be read- only.
For example, when creating a new Item
model, all fields must be editable,
but while updating the record, is there a way to disable the sku
field so
that it is visible, but cannot be edited?
class Item(models.Model):
sku = models.CharField(max_length=50)
description = models.CharField(max_length=200)
added_by = models.ForeignKey(User)
class ItemForm(ModelForm):
class Meta:
model = Item
exclude = ('added_by')
def new_item_view(request):
if request.method == 'POST':
form = ItemForm(request.POST)
# Validate and save
else:
form = ItemForm()
# Render the view
Can class ItemForm
be reused? What changes would be required in the
ItemForm
or Item
model class? Would I need to write another class,
"ItemUpdateForm
", for updating the item?
def update_item_view(request):
if request.method == 'POST':
form = ItemUpdateForm(request.POST)
# Validate and save
else:
form = ItemUpdateForm()
Answer
As pointed out in [this answer](https://stackoverflow.com/questions/324477/in- a-django-form-how-do-i-make-a-field-readonly-or-disabled-so-that-it- cannot/34538169#34538169), Django 1.9 added the Field.disabled attribute:
The disabled boolean argument, when set to True, disables a form field using the disabled HTML attribute so that it won’t be editable by users. Even if a user tampers with the field’s value submitted to the server, it will be ignored in favor of the value from the form’s initial data.
With Django 1.8 and earlier, to disable entry on the widget and prevent
malicious POST hacks you must scrub the input in addition to setting the
readonly
attribute on the form field:
class ItemForm(ModelForm):
def __init__(self, *args, **kwargs):
super(ItemForm, self).__init__(*args, **kwargs)
instance = getattr(self, 'instance', None)
if instance and instance.pk:
self.fields['sku'].widget.attrs['readonly'] = True
def clean_sku(self):
instance = getattr(self, 'instance', None)
if instance and instance.pk:
return instance.sku
else:
return self.cleaned_data['sku']
Or, replace if instance and instance.pk
with another condition indicating
you're editing. You could also set the attribute disabled
on the input
field, instead of readonly
.
The clean_sku
function will ensure that the readonly
value won't be
overridden by a POST
.
Otherwise, there is no built-in Django form field which will render a value
while rejecting bound input data. If this is what you desire, you should
instead create a separate ModelForm
that excludes the uneditable field(s),
and just print them inside your template.