3

In Django admin, if I have a model field that's a TextField and set it as readonly using readonly_fields, then it's displayed as text in a <p> tag.

I'd like it to still be displayed as a textarea field, but with its disabled attribute set.

What's the simplest way to accomplish this?

Phil Gyford
  • 13,432
  • 14
  • 81
  • 143

4 Answers4

8

use a form field

   somefield = forms.CharField(
       widget=forms.TextInput(attrs={'readonly':'readonly'})
    )
cinoch
  • 131
  • 1
  • 7
3

A bit late, but here's an idea (inspired by @cinoch`s answer and this answer) that does the trick for me, with a minimum of code:

  1. do not add the name of your TextField to the readonly_fields in your ModelAdmin subclass (otherwise step 2 has no effect)

  2. instead, do add the following to your ModelAdmin subclass:

    formfield_overrides = {
        TextField: dict(widget=Textarea(attrs=dict(readonly=True)))
    }
    

Note this requires some imports:

from django.db.models import TextField
from django.forms import Textarea

The TextField will now show up on the admin page as a scrollable Textarea instead of plain text, and its content will now be read-only, as desired.

Downside is that this applies to all TextFields in the model. If that's a problem, you should probably use a custom form as suggested by @cinoch and described in more detail here or here.

Also, this has no effect if ModelAdmin.has_change_permission() returns False.

djvg
  • 11,722
  • 5
  • 72
  • 103
2

@alasdair's answer is actually quite clever, but, unfortunately, it does not provide an example.

Here's my attempt to clarify, based on the docs for readonly_fields.

Assuming a model like so:

class MyModel(models.Model):
    my_textfield = models.TextField()

The admin could look like this, using format_html to create a readonly textarea:

class MyModelAdmin(admin.ModelAdmin):
    exclude = ['my_textfield']
    readonly_fields = ['display_my_textfield']
    
    @admin.display(description='my textfield')
    def display_my_textfield(self, obj):
        return format_html(
            '<textarea cols="40" rows="10" readonly>{}</textarea>',
            obj.my_textfield)

This also works if ModelAdmin.has_change_permission() returns False.

djvg
  • 11,722
  • 5
  • 72
  • 103
  • It would be good if this answer was more delineated, I'm not sure how to implement this and get it working. – Conor Jun 30 '22 at 10:49
  • @Conor: Not sure what more to tell. You need to import the required modules and register `MyModelAdmin` for `MyModel` as described in the [ModelAdmin docs](https://docs.djangoproject.com/en/4.0/ref/contrib/admin/#modeladmin-objects). – djvg Jun 30 '22 at 11:04
  • Yes it's fine, I think I understand the concept. I'm just wondering why you use exclude = ['my_textfield'], and then make a function to render a readonly textarea when it already becomes hidden in the admin panel – Conor Jun 30 '22 at 11:19
  • @Conor: See [@alasdair's answer](https://stackoverflow.com/a/33394770) for that. If you don't use `exclude`, you will see two fields in the admin, one readonly and one that can be changed. – djvg Jun 30 '22 at 11:24
  • Ok I get it, I was thinking black and white about having just an empty text box. But it could be worthwhile to make any text readonly if that was the desire. Thanks for replying. – Conor Jun 30 '22 at 11:28
1

The readonly_fields can take method names as well as field names. You could write a method that renders the value of the field in a disabled textarea.

Make sure you exclude the field from the model admin, since it will no longer be in readonly_fields.

Alasdair
  • 298,606
  • 55
  • 578
  • 516