Django Creating A Form Field That's Read Only Using Widgets
Solution 1:
You should use a form field and not a model field:
somefield = models.CharField(
widget=forms.TextInput(attrs={'readonly': 'readonly'})
)
replaced with
somefield = forms.CharField(
widget=forms.TextInput(attrs={'readonly': 'readonly'})
)
Should fix it.
Solution 2:
Note that the readonly
attribute does not keep Django from processing any value sent by the client. If it is important to you that the value doesn't change, no matter how creative your users are with FireBug, you need to use a more involved method, e.g. a ReadOnlyField
/ReadOnlyWidget
like demonstrated in a blog entry by Alex Gaynor.
Solution 3:
I was going into the same problem so I created a Mixin that seems to work for my use cases.
classReadOnlyFieldsMixin(object):
readonly_fields =()
def__init__(self, *args, **kwargs):
super(ReadOnlyFieldsMixin, self).__init__(*args, **kwargs)
for field in (field for name, field in self.fields.iteritems() if name in self.readonly_fields):
field.widget.attrs['disabled'] = 'true'
field.required = Falsedefclean(self):
cleaned_data = super(ReadOnlyFieldsMixin,self).clean()
for field in self.readonly_fields:
cleaned_data[field] = getattr(self.instance, field)
return cleaned_data
Usage, just define which ones must be read only:
class MyFormWithReadOnlyFields(ReadOnlyFieldsMixin, MyForm):
readonly_fields = ('field1', 'field2', 'fieldx')
Solution 4:
As Benjamin (https://stackoverflow.com/a/2359167/565525) nicely explained, additionally to rendering correctly, you need to process field on backend properly.
There is an SO question and answers that has many good solutions. But anyway:
1) first approach - removing field in save() method, e.g. (not tested ;) ):
defsave(self, *args, **kwargs):
for fname in self.readonly_fields:
if fname in self.cleaned_data:
del self.cleaned_data[fname]
returnsuper(<form-name>, self).save(*args,**kwargs)
2) second approach - reset field to initial value in clean method:
def clean_<fieldname>(self):
returnself.initial[<fieldname>] # or getattr(self.instance, <fieldname>)
Based on second approach I generalized it like this:
from functools import partial
class <Form-name>(...):
def__init__(self, ...):
...
super(<Form-name>, self).__init__(*args, **kwargs)
...
for i, (fname, field) inenumerate(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 notindir(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)
Post a Comment for "Django Creating A Form Field That's Read Only Using Widgets"