Skip to content Skip to sidebar Skip to footer

Django Creating A Form Field That's Read Only Using Widgets

My form field looks something like the following: class FooForm(ModelForm): somefield = models.CharField( widget=forms.TextInput(attrs={'readonly':'readonly'}) )

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"