Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating a display-only (non-editable) Django admin field

Is it possible to build a custom model field/widget combination which displays a value but never writes anything back to the database? I would use this widget exclusively in the admin's forms.

I wrote my own field, which overwrites the formfield() method to declare its own widget class. It displays just fine, but as soon as the 'Save' button is clicked in the admin, I'm getting a validation error:

This field is required.

That makes sense, considering that my widget didn't render out a form field. However, what I'd like to do is basically remove this field from the update process: whenever used in the admin, it just shouldn't be mentioned in the SQL UPDATE at all.

Is that possible?

Here's a sketch of the code I have so far:

class MyWidget(Widget):
    def render(self, name, value, attrs=None):
        if value is None:
            value = ""
        else:
            # pretty print the contents of value here
            return '<table>' + ''.join(rows) + '</table>'

class MyField(JSONField):
    def __init__(self, *args, **kwargs):
        kwargs['null'] = False
        kwargs['default'] = list
        super(MyField, self).__init__(*args, **kwargs)

    def formfield(self, **kwargs):
        defaults = {
            'form_class': JSONFormField,
            'widget': MyWidget,
        }
        defaults.update(**kwargs)
        return super(MyField, self).formfield(**defaults)

UPDATE 1: The use case is that the field represents an audit log. Internally, it will be written to regularly. The admin however never needs to write to it, it only has to render it out in a very readable format.

I'm not using any other ModelForms in the application, so the admin is the only form-user. I don't want to implement the behavior on the admin classes themselves, because this field will be reused across various models and is always supposed to behave the same way.

like image 735
Henrik Heimbuerger Avatar asked Nov 03 '15 21:11

Henrik Heimbuerger


1 Answers

There are multiple ways to create a read-only field in the admin pages. Your requirements on the database storage are a bit fuzzy so I go through the options.

You have to register an AdminModel first in admin.py:

from django.contrib import admin
from yourapp.models import YourModel

class YourAdmin(admin.ModelAdmin):
    pass

admin.site.register(YourModel, YourAdmin)

Now you can add different behavior to it. For example you can add the list of fields shown in the edit/add page:

class YourAdmin(admin.ModelAdmin):
    fields = ['field1', 'field2']

This can be names of the model fields, model properties or model methods. Methods are displayed read-only.

If you want to have one field read-only explicitly add this:

class YourAdmin(admin.ModelAdmin):
    fields = ['field1', 'field2']
    readonly_fields = ['field2']

Then you have the option to overwrite the display of the field completely by adding a method with the same name. You will not even need a model field/method with that name, then:

class YourAdmin(admin.ModelAdmin):
    fields = ['field1', 'field2']
    readonly_fields = ['field2']

    def field2(self, obj):
        return '*** CLASSIFIED *** {}'.format(obj.field2)

With django.utils.safestring.mark_safe you can return HTML code as well.

All other options of the Admin are available, except the widget configuration as it applies to the writable fields only.

like image 88
Klaus D. Avatar answered Oct 08 '22 19:10

Klaus D.