from django.db import models # Decorator for django models that contain readonly fields. def has_readonly_fields(original_class): def store_read_only_fields(sender, instance, **kwargs): if not instance.id: return for field_name in sender.read_only_fields: val = getattr(instance, field_name) setattr(instance, field_name + "_oldval", val) def check_read_only_fields(sender, instance, **kwargs): if not instance.id: return for field_name in sender.read_only_fields: old_value = getattr(instance, field_name + "_oldval") new_value = getattr(instance, field_name) if old_value != new_value: raise ValueError("Field %s is read only." % field_name) models.signals.post_init.connect(store_read_only_fields, original_class, weak=False) # for load models.signals.post_save.connect(store_read_only_fields, original_class, weak=False) # for save models.signals.pre_save.connect(check_read_only_fields, original_class, weak=False) return original_class # decorator for django modelforms that contain readonly fields def modelform_with_readonly_fields(origin_class): origin_init = origin_class.__init__ def lambder(field, old_method): return lambda this : clean_field(this, field, old_method) def clean_field(self, field_name, old_method): instance = getattr(self, 'instance', None) if instance and instance.id: return getattr(instance, field_name) else: if old_method: return old_method(self) else: return self.cleaned_data.get(field_name, None) def __init__(self, *args, **kws): origin_init(self, *args, **kws) instance = getattr(self, 'instance', None) if instance and instance.id: for field in origin_class.Meta.model.read_only_fields: self.fields[field].required = False self.fields[field].widget.attrs['disabled'] = 'disabled' origin_class.__init__ = __init__ for field in origin_class.Meta.model.read_only_fields: field_name = "clean_" + field old_method = getattr(origin_class, field_name, None) setattr(origin_class, field_name, lambder(field, old_method)) return origin_class