Как хорошо объяснил Бенджамин (https://stackoverflow.com/a/2359167/565525), помимо правильного рендеринга, вам нужно правильно обработать поле на бэкенде.
Существует вопрос SO и ответы , в котором есть много хороших решений. Но все равно:
1) первый подход - удаление поля в методе save (), например (не проверено;)):
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) второй подход - сброс поля к начальному значению в чистом методе:
def clean_<fieldname>(self):
return self.initial[<fieldname>] # or getattr(self.instance, <fieldname>)
Основываясь на втором подходе, я обобщил это так:
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)