Login

Readonly admin fields

Author:
peritus
Posted:
August 1, 2008
Language:
Python
Version:
.96
Tags:
django admin field readonly
Score:
11 (after 11 ratings)

Put this code and import it where you define your ModelAdmin-classes.

# typical admin.py file:
from django.contrib import admin
from foo.bar import ReadOnlyAdminFields

class MyModelAdmin(ReadOnlyAdminFields, admin.ModelAdmin):
    readonly = ('field1', 'field2',)
 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
from django import forms

class ReadOnlyWidget(forms.Widget):
    def __init__(self, original_value, display_value):
        self.original_value = original_value
        self.display_value = display_value

        super(ReadOnlyWidget, self).__init__()

    def render(self, name, value, attrs=None):
        if self.display_value is not None:
            return unicode(self.display_value)
        return unicode(self.original_value)

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

class ReadOnlyAdminFields(object):
    def get_form(self, request, obj=None):
        form = super(ReadOnlyAdminFields, self).get_form(request, obj)

        if hasattr(self, 'readonly'):
            for field_name in self.readonly:
                if field_name in form.base_fields:

                    if hasattr(obj, 'get_%s_display' % field_name):
                        display_value = getattr(obj, 'get_%s_display' % field_name)()
                    else:
                        display_value = None

                    form.base_fields[field_name].widget = ReadOnlyWidget(getattr(obj, field_name, ''), display_value)
                    form.base_fields[field_name].required = False

        return form

More like this

Comments

mredar (on August 26, 2008):
<p>This worked great. Should be part of the main Django branch IMHO, seems to be a bit of functionality that is needed quite often!</p>

#

djbe (on August 27, 2008):
<p>It works - but when I try to update existing data via admin interface, I get blocked and receive the errormessage:</p> <p>"Enter a list of values."</p> <p>This happens on a DateTime field. I have not tried others so far.</p>

#

ekellner (on August 28, 2008):
<p>I think this needs to output a hidden input field on render, or else the form that this belongs to will not validate. I think just by inheriting from widget.HiddenInput and calling super on render, it should (basically) work.</p>

#

ekellner (on August 28, 2008):
<p>I see, it doesn't use a hidden field because it intercepts the value_from_datadict call to the widget. Unfortunately, when the field owning the widget expects a form value isn't the same as the object property value -- as is the case for widgets that post more than 1 value, or foreign key values where the field expects an id -- the widget doesn't provide the value in the right format for the field.</p>

#

rickvanderzwet (on November 30, 2008):
<p>Will there be some way to also enable this very useful fields in InlineForms as well? As snippet below, does not seems to fix it. </p> <pre># typical admin.py file: from django.contrib import admin from foo.bar import ReadOnlyAdminFields class MyModelInline(ReadOnlyAdminFields, admin.TabularInline): readonly = ('fieldFoo', 'fieldBar',) class MyModelAdmin(ReadOnlyAdminFields,admin.ModelAdmin): readonly = ('field1', 'field2',) inlines = (MyModelInline, ) </pre>

#

line (on July 23, 2009):
<p>If you want activate this feature only for a specific user group, put this code and import it where you define your ModelAdmin-classes.</p> <pre># typical admin.py file: from django.contrib import admin from foo.bar import ReadOnlyAdminFields class MyModelAdmin(ReadOnlyAdminFields, admin.ModelAdmin): def get_form(self, request, obj=None, **kwargs): if request.user.is_authenticated(): ReadOnlyAdminFields.readonly = ('',) for group in request.user.groups.all(): if str(group) == 'LimitedGroup': ReadOnlyAdminFields.readonly = ('field1', 'field2',) return super(SetAdmin, self).get_form(request, obj, **kwargs) </pre> <p>Limitation: the name of the group (LimitedGroup) is hard coded here, which is not recommended. But it still provides some hints on how to do it. Man can imagine a simple model class to add/configure those limited groups.</p> <p>If you have some better code feel free to share! ;)</p>

#

invitro (on October 11, 2009):
<p>To make it work with ForeignKey or ManyToManyField, I changed line 27-31:</p> <pre># field with 'choices' attribute if hasattr( obj, 'get_%s_display' % field_name ): original_value = getattr( obj, field_name, '' ) display_value = getattr( obj, 'get_%s_display' % field_name )() # ForeignKey or ManyToManyField elif hasattr( obj, '%s_id' % field_name ): original_value = getattr( obj, "%s_id" % field_name, '' ) display_value = getattr( obj, field_name, '' ) # other field type else: original_value = getattr( obj, field_name, '' ) display_value = getattr( obj, field_name, '' ) </pre> <p>I hope it helps</p>

#

invitro (on October 11, 2009):
<p>EDIT: I forgot to tell this, change the code after those line and this is complete code of "ReadOnlyAdminFields"</p> <pre>class ReadOnlyAdminFields( object ): def get_form( self, request, obj = None ): form = super( ReadOnlyAdminFields, self ).get_form( request, obj ) if hasattr( self, 'readonly' ): for field_name in self.readonly: if field_name in form.base_fields: # field with 'choices' attribute if hasattr( obj, 'get_%s_display' % field_name ): original_value = getattr( obj, field_name, '' ) display_value = getattr( obj, 'get_%s_display' % field_name )() # ForeignKey or ManyToManyField elif hasattr( obj, '%s_id' % field_name ): original_value = getattr( obj, "%s_id" % field_name, '' ) display_value = getattr( obj, field_name, '' ) # other field type else: original_value = getattr( obj, field_name, '' ) display_value = getattr( obj, field_name, '' ) form.base_fields[field_name].widget = ReadOnlyWidget( original_value, display_value ) form.base_fields[field_name].required = False </pre>

#

niran (on December 20, 2009):
<p>get_form() should accept arbitrary kwargs as well to match the signature of the function being overridden.</p>

#

seddonym (on January 18, 2010):
<p>I couldn't get this to work - I get the following error:</p> <p>'NoneType' object is not callable</p> <p>Traceback: File "/usr/local/lib/python2.6/dist-packages/django/core/handlers/base.py" in get_response 99. response = callback(request, callback_args, callback_kwargs) File "/usr/local/lib/python2.6/dist-packages/django/contrib/admin/options.py" in wrapper 237. return self.admin_site.admin_view(view)(*args, kwargs) File "/usr/local/lib/python2.6/dist-packages/django/utils/decorators.py" in call 36. return self.decorator(self.func)(args, kwargs) File "/usr/local/lib/python2.6/dist-packages/django/utils/decorators.py" in _wrapped_view 86. response = view_func(request, *args, kwargs) File "/usr/local/lib/python2.6/dist-packages/django/utils/decorators.py" in call 36. return self.decorator(self.func)(args, kwargs) File "/usr/local/lib/python2.6/dist-packages/django/views/decorators/cache.py" in _wrapped_view_func 70. response = view_func(request, *args, kwargs) File "/usr/local/lib/python2.6/dist-packages/django/contrib/admin/sites.py" in inner 187. return view(request, args, kwargs) File "/usr/local/lib/python2.6/dist-packages/django/utils/decorators.py" in _wrapped_view 86. response = view_func(request, *args, kwargs) File "/usr/local/lib/python2.6/dist-packages/django/db/transaction.py" in _commit_on_success 295. res = func(args, *kw) File "/usr/local/lib/python2.6/dist-packages/django/contrib/admin/options.py" in change_view 894. form = ModelForm(instance=obj)</p> <p>Exception Type: TypeError at /admin/snippets/snippet/1/ Exception Value: 'NoneType' object is not callable</p>

#

FarmKing (on March 18, 2013):
<p>This appears to now be built in to django.contrib.admin as readonly_fields and works with StackedInline and TabularInline in addition to ModelAdmin.</p> <p>https://github.com/django/django/blob/master/django/contrib/admin/options.py</p>

#

Please login first before commenting.