Login

Readonly fields on Form/Modelform

Author:
Killarny
Posted:
February 24, 2009
Language:
Python
Version:
1.0
Score:
1 (after 1 ratings)

A Form and ModelForm which provides the ability to specify certain fields as readonly, meaning that they will display their value as text wrapped with a <span> tag. The user is unable to edit them, and they are protected from POST data insertion attacks.

The recommended usage is to place a NewMeta inner class on the form, with a readonly attribute which is a list or tuple of fields, similar to the fields and exclude attributes on the Meta inner class.

class MyForm(ReadonlyForm):
    foo = forms.TextField()
    bar = forms.TextField()
    class NewMeta:
        readonly = ('foo',)

Use these forms as you would a standard form in your templates.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
from django import forms
from django.utils.safestring import mark_safe

class SpanWidget(forms.Widget):
    '''Renders a value wrapped in a <span> tag.
    
    Requires use of specific form support. (see ReadonlyForm 
    or ReadonlyModelForm)
    '''

    def render(self, name, value, attrs=None):
        final_attrs = self.build_attrs(attrs, name=name)
        return mark_safe(u'<span%s >%s</span>' % (
            forms.util.flatatt(final_attrs), self.original_value))

    def value_from_datadict(self, data, files, name):
        return self.original_value

class SpanField(forms.Field):
    '''A field which renders a value wrapped in a <span> tag.
    
    Requires use of specific form support. (see ReadonlyForm 
    or ReadonlyModelForm)
    '''
    
    def __init__(self, *args, **kwargs):
        kwargs['widget'] = kwargs.get('widget', SpanWidget)
        super(SpanField, self).__init__(*args, **kwargs)

class Readonly(object):
    '''Base class for ReadonlyForm and ReadonlyModelForm which provides
    the meat of the features described in the docstings for those classes.
    '''

    class NewMeta:
        readonly = tuple()

    def __init__(self, *args, **kwargs):
        super(Readonly, self).__init__(*args, **kwargs)
        readonly = self.NewMeta.readonly
        if not readonly:
            return
        for name, field in self.fields.items():
            if name in readonly:
                field.widget = SpanWidget()
            elif not isinstance(field, SpanField):
                continue
            field.widget.original_value = str(getattr(self.instance, name))

class ReadonlyForm(Readonly, forms.Form):
    '''A form which provides the ability to specify certain fields as
    readonly, meaning that they will display their value as text wrapped
    with a <span> tag. The user is unable to edit them, and they are
    protected from POST data insertion attacks.
    
    The recommended usage is to place a NewMeta inner class on the
    form, with a readonly attribute which is a list or tuple of fields,
    similar to the fields and exclude attributes on the Meta inner class.
    
        class MyForm(ReadonlyForm):
            foo = forms.TextField()
            class NewMeta:
                readonly = ('foo',)
    '''
    pass

class ReadonlyModelForm(Readonly, forms.ModelForm):
    '''A ModelForm which provides the ability to specify certain fields as
    readonly, meaning that they will display their value as text wrapped
    with a <span> tag. The user is unable to edit them, and they are
    protected from POST data insertion attacks.
    
    The recommended usage is to place a NewMeta inner class on the
    form, with a readonly attribute which is a list or tuple of fields,
    similar to the fields and exclude attributes on the Meta inner class.
    
        class Foo(models.Model):
            bar = models.CharField(max_length=24)

        class MyForm(ReadonlyModelForm):
            class Meta:
                model = Foo
            class NewMeta:
                readonly = ('bar',)
    '''
    pass

More like this

  1. Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
  2. JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
  3. Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
  4. Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
  5. Help text hyperlinks by sa2812 1 year, 8 months ago

Comments

filias (on February 16, 2010):

Hi,

it works for me with dropdown lists and text fields.

how can I make it work with checkboxes?

Thanks.

#

jamida (on May 24, 2010):

This is not working at all for me. It works "fine" up until I try to save a modelform with some fields as read-only. Something in the system tries to verify the readonly fields and I am getting errors.

I'm new to django, and since no one else is complaining about this perhaps it's my code, but I can't figure it out.

#

offby1 (on August 17, 2010):

This doesn't work at all, at least not the base Readonly object, without a ModelForm.

The base Form class doesn't have an 'instance' property.

#

offby1 (on February 28, 2011):

It's even a bit trickier than that; this won't work with initial data, either; you have to actually populate the form with data for this to work.

#

perrigo (on October 26, 2011):

Old snippet I know, but I have a fix for most field types (actually all of the ones I've tested including foreign keys and choice fields).

Replace Line 14:

forms.util.flatatt(final_attrs), self.display_value))

Replace Line 48:

model_field = self.instance._meta.get_field_by_name(name)[0]
field.widget.original_value = model_field.value_from_object(self.instance)
field.widget.display_value = self.instance.get_field_display(name)

#

Please login first before commenting.