Como Benjamin (https://stackoverflow.com/a/2359167/565525) bien explicado, además de la representación correcta, debe procesar el campo en el backend correctamente.
Hay un SO question and answers que tiene muchas buenas soluciones. Pero de todos modos:
1) primer acercamiento - eliminación del campo en el método save(), p. (No probado;)):
def save(self, *args, **kwargs):
for fname in self.readonly_fields:
if fname in self.cleaned_data:
del self.cleaned_data[fname]
return super(<form-name>, self).save(*args,**kwargs)
2) segundo enfoque - campo de restablecimiento a valores iniciales en el método limpia:
def clean_<fieldname>(self):
return self.initial[<fieldname>] # or getattr(self.instance, <fieldname>)
Basado en segundo enfoque que generalicé así:
from functools import partial
class <Form-name>(...):
def __init__(self, ...):
...
super(<Form-name>, self).__init__(*args, **kwargs)
...
for i, (fname, field) in enumerate(self.fields.iteritems()):
if fname in self.readonly_fields:
field.widget.attrs['readonly'] = "readonly"
field.required = False
# set clean method to reset value back
clean_method_name = "clean_%s" % fname
assert clean_method_name not in dir(self)
setattr(self, clean_method_name, partial(self._clean_for_readonly_field, fname=fname))
def _clean_for_readonly_field(self, fname):
""" will reset value to initial - nothing will be changed
needs to be added dynamically - partial, see init_fields
"""
return self.initial[fname] # or getattr(self.instance, fname)
Esto cambió en Django 1.9 https://stackoverflow.com/questions/324477/in-a-django-form-how -do-i-make-a-field-readonly-or-disabled-so-that-it-can not – zudebluvstein