8

I have following model:

class Model(models.Model):

    creator = models.ForeignKey(User,related_name='com_creator',on_delete=models.SET_NULL, blank=True, null=True)
    username = models.CharField(max_length=60,default="")
    created = models.DateTimeField(auto_now_add=True)
    body = models.TextField(max_length=10000,default=" ")
    subtype = models.CharField(_("SubType"),max_length=100)
    typ = models.CharField(_("Type"),max_length=50)
    plus = models.ManyToManyField(User,related_name='com_plus', verbose_name=_('Plus'), blank=True)
    is_anonymous = models.BooleanField(_('Stay Anonymous'), blank=True, default=False)

The values in typ and subtype are codes, like: "6_0_p", (because the original values are ridiculosly long, so I use codes and a dict to translate to human readable form).

QUESTION: How can I intercept these values in django admin and translate them to human readable form?

This is what i tried so far:

class ModelAdmin(admin.ModelAdmin):
    model = Model
    extra = 1
    exclude = ("username","creator","plus" ) 
    readonly_fields = ('subtype','typ','is_anonymous','created')
    fields = ('body','subtype','typ')

    def typ(self, obj):
        self.typ = "ppp"
        obj.typ = "ppp"
        return "pppp"

I tried returning the object, self, some other value. I also tried to set the value without using a callable just declaring "typ='xxx'". Nothing. I probably don't understand how this whole thing works ...

Any ideas will be appreciated.

Stefan
  • 828
  • 12
  • 24

3 Answers3

8

You need to create a readonly_field which corresponds to your method name, then return anything from that method.

Documentation here: https://docs.djangoproject.com/en/1.11/ref/contrib/admin/#django.contrib.admin.ModelAdmin.readonly_fields

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

    def my_custom_field(self, obj):
        return 'Return Anything Here'
Yuji 'Tomita' Tomita
  • 115,817
  • 29
  • 282
  • 245
  • I do have this approach in my example code. It's not doing it. – Stefan Jun 07 '17 at 19:10
  • 3
    @stefan it won't work if you have a database field named the same. Name the method something other than your existing field, and put that in readonly_fields. Paste the above example and see it work. Add `my_custom_field` to the end of your `readonly_fields`, and add a method named the same to see it in action. – Yuji 'Tomita' Tomita Jun 07 '17 at 19:11
  • Great clarification @Yuji'Tomita'Tomita - now how to I make the returned value a HTML? I want to display a list. – Lenka Pitonakova May 11 '22 at 17:01
  • @Elendurwen see here https://stackoverflow.com/questions/3298083/prevent-django-admin-from-escaping-html -- depends on version. – Yuji 'Tomita' Tomita May 12 '22 at 19:04
5

I think that your code doesn't work because the method name is the same as the field.

You have to change the name of field, like _typ in fields tuple, then add a method called _typ that return anything or some obj attr.

i.e:

class ModelAdmin(admin.ModelAdmin):
    model = Model
    extra = 1
    exclude = ("username","creator","plus" ) 
    readonly_fields = ('subtype','typ','is_anonymous','created')
    fields = ('body','subtype','_typ')

    def _typ(self, obj):
        return "pppp"
Diego Puente
  • 1,964
  • 20
  • 23
  • It says now: "FieldError: Unknown field(s)" – Stefan Jun 07 '17 at 18:57
  • 1
    Yuji below explained to me in more detail. Your answer is technically also correct, but I understood it from hys effort. Many thanks to you as well. – Stefan Jun 07 '17 at 19:16
1

In Django 4 and above you can add a custom (readonly) field easily, like this:

class FooAdmin(admin.ModelAdminAbstractAdmin):
    readonly_fields = [
        '_dynamic_field',
    ]
    fieldsets = [
        ('Form', {
            'fields': (
                '_dynamic_field',
            ),
            'classes': ('form-group',)
        }),
    ]

    def _dynamic_field(self, obj=None):
        if obj is not None:
            return 'Bar'
        return "Foo"

    # to change the label
    _dynamic_field.__name__ = "Dynamic field label"

How you can see, the field needs to be in readonly_fields and fieldsets too.

Enoque
  • 57
  • 7