Krachtwerk
Krachtwerk

Reputation: 416

Read-only form field formatting

I want to display a field as read only in a ModelAdmin form, so I added it to the readonly_fields attribute.

However, since the field contains a currency, stored as an integer, I want to apply some nice formatting it. I've created a custom ModelForm for my ModelAdmin, trying to apply the formatting in the overridden __init__ method.

The problem is, I cannot find the value. The field is not present in the self.fields attribute.

Does anyone know where the values for the readonly_fields are kept, or is there a better/different approach?

Upvotes: 2

Views: 1887

Answers (3)

Greg Eremeev
Greg Eremeev

Reputation: 1840

Another, more appropriate solution, works in Django 2.1.2:

ModelAdmin renders read-only fields via special wrapper AdminReadonlyField (django/contrib/admin/helpers.py) if we look at contents method, we can see the code

if getattr(widget, 'read_only', False):
    return widget.render(field, value)

It means that if a widget has read_only attribute with True value then the read-only field will invoke widget's render method. Hence, you can use render method to format your value.

For example:

class CustomDateInput(widgets.DateInput):
    read_only = True

    def _render(self, template_name, context, renderer=None):
        return 'you value'

class CustomForm(forms.ModelForm):
    some_field = forms.DateTimeField(widget=CustomDateInput())

@admin.register(SomeModel)
class SomeModelAdmin(admin.ModelAdmin):
    form = CustomForm    
    readonly_fields = ['some_field']

Upvotes: 3

Burhan Khalid
Burhan Khalid

Reputation: 174698

An alternate approach, which works for all types of forms is to create a widget to represent a read only field. Here is one that I wrote for my own use. You can change the <span %s>%s</span> to suit your own requirements.

from django import forms
from django.utils.safestring import mark_safe
from django.utils.encoding import force_unicode


class ReadOnlyWidget(forms.TextInput):
    def render(self, name, value, attrs=None):
         if value is None:
             value = ''
         final_attrs = self.build_attrs(attrs, type=self.input_type, name=name)
         if value != '':
             # Only add the 'value' attribute if a value is non-empty.
             final_attrs['value'] = force_unicode(self._format_value(value))
         return mark_safe(u'<span%s />%s</span>' % (flatatt(final_attrs),value))

Once you have that added, simply do this:

class MyAdmin(admin.ModelAdmin):
    foo = models.TextField(widget=ReadOnlyWidget(attrs={'class':'read-only'}
                           initial="$50")

Then in your CSS, do some styling for a read-only class, or you can adjust the attributes accordingly.

Upvotes: 4

Just do something like:

class MyAdmin(admin.ModelAdmin):
    readonly_fields = ('foo',)

    def foo(self, obj):
        return '${0}'.format(obj.amount)

Upvotes: 3

Related Questions