Skip to content

Instantly share code, notes, and snippets.

@edgabaldi
Created July 16, 2015 02:31
Show Gist options
  • Save edgabaldi/5b7ccb32195d2d87f083 to your computer and use it in GitHub Desktop.
Save edgabaldi/5b7ccb32195d2d87f083 to your computer and use it in GitHub Desktop.
ReadOnlyFieldsMixin
from django.utils import six
from django.utils.encoding import force_str
class ReadOnlyFieldsMixin(object):
readonly_fields = ()
def __init__(self, *args, **kwargs):
super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
self.define_readonly_fields(self.fields)
def clean(self):
cleaned_data = super(ReadOnlyFieldsMixin, self).clean()
for field_name, field in six.iteritems(self.fields):
if self._must_be_readonly(field_name):
cleaned_data[field_name] = getattr(self.instance, field_name)
return cleaned_data
def define_readonly_fields(self, field_list):
fields = [field for field_name, field in six.iteritems(field_list)
if self._must_be_readonly(field_name)]
map(lambda field: self._set_readonly(field), fields)
def _all_fields(self):
return not bool(self.readonly_fields)
def _set_readonly(self, field):
field.widget.attrs['disabled'] = 'true'
field.required = False
def _must_be_readonly(self, field_name):
return field_name in self.readonly_fields or self._all_fields()
def new_readonly_form_class(klass, all_fields=False, readonly_fields=()):
name = force_str("ReadOnly{}".format(klass.__name__))
klass_fields = {'all_fields': all_fields, 'readonly_fields': readonly_fields}
return type(name, (ReadOnlyFieldsMixin, klass), klass_fields)
from django.test import TestCase
from django.forms import ModelForm
from django.db import models
from django.utils import six
from readonly import ReadOnlyFieldsMixin
class Dummy(models.Model):
foo = models.CharField(max_length=100)
bar = models.CharField(max_length=100)
class FooModelForm(ReadOnlyFieldsMixin, ModelForm):
class Meta:
model = Dummy
class BarModelForm(ReadOnlyFieldsMixin, ModelForm):
readonly_fields = ('foo',)
class Meta:
model = Dummy
class ReadOnlyFieldsMixinTestCase(TestCase):
def test_readonly_fields_attr_empty_all_fields_should_be_readonly(self):
form = FooModelForm()
self.assertEqual(['foo', 'bar'], self._get_readonly_fields(form))
def test_readonly_fields_just_defined_fields_should_be_readonly(self):
form = BarModelForm()
self.assertEqual(['foo',], self._get_readonly_fields(form))
def test_clean_all_fields_readonly(self):
form = FooModelForm({'foo':'FOO'}) # bar empty is invalid
form.is_valid()
self.assertEqual(['foo', 'bar'], self._get_fields_cleaned(form))
def test_clean_attr_empty_all_fields_0(self):
form = BarModelForm({'foo': 'FOO'})
form.is_valid()
self.assertEqual(['foo'], self._get_fields_cleaned(form))
self.assertTrue('bar' in form.errors.keys())
def test_clean_attr_empty_all_fields_1(self):
form = BarModelForm({'bar': 'BAR'})
form.is_valid()
self.assertEqual(['foo', 'bar'], self._get_fields_cleaned(form))
def _widget_attr_is_disabled(self, field):
return 'disabled' in field.widget.attrs and \
field.widget.attrs['disabled'] == 'true'
def _is_readonly(self, field):
return self._widget_attr_is_disabled(field) and not field.required
def _get_readonly_fields(self, form):
return [field_name for field_name, field in six.iteritems(form.fields)
if self._is_readonly(field)]
def _get_fields_cleaned(self, form):
return [field_name for field_name, field in
six.iteritems(form.cleaned_data)]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment