Django 3.x: AttributeError: module 'django.contrib.admin' has no attribute 'display'
Asked Answered
F

5

5
from django.contrib import admin
from .models import Shop

@admin.register(Shop)
class ShopAdmin(admin.ModelAdmin):
 

     @admin.display(description='Name')
     def upper_case_name(self,obj):
         return("%s" % (obj.name)).upper()   

  
Furlough answered 6/5, 2021 at 12:29 Comment(5)
What version of Django are you using? The display decorator was introduced in Django 3.2 and it doesn't exist in older versions.Rosenblum
i use django version 3.1.9. Can i change my django version while working inside my older projectFurlough
I am using django3.2 but still shows same error.Irredentist
Are you using vscode for django project. Then you should specify interpreter path in vscode otherwise django will show different version in vscode.Furlough
Same error, in Django 3.9.Lamppost
S
3

Django==3.1

I was fighting with this for a while, I didn't understand why the @admin.action() decorator didn't appear, I started to read the Django code and I found that when the actions for the Django Admin start to load, it calls a method called _get_base_actions, and this same method gets the action and description in this way:

# django.contrib.admin.ModelAdmin._get_base_actions
...
description = getattr(func, 'short_description', name.replace('_', ' '))
...
    

I had already tried several ways to load the action and to correctly take the description of the action, if it was not defined, what Django did was to take the name of the same function and replace the underscores with spaces, resulting in a somewhat frightening result.

The first thing I thought of as a quick and valid solution was to create a class and modify the __new__ method, which is called before the __init__ method.

This is an example of what I needed, to be able to generate a PDF from Django actions:

class GeneratePDF:
    short_description = "WRITE THE DESCRIPTION OF ACTION HERE"

    def __new__(cls, modeladmin, request, queryset):
        result = cls.generate_pdf_resume(modeladmin, request, queryset)
        return result

    @classmethod
    def generate_pdf_resume(cls, modeladmin, request, queryset):
        ...

Finally, I added it to the list of ModelAdmin actions.


from project.own_actions import GeneratePDF

@admin.register(Patient)
class PatientAdmin(admin.ModelAdmin):
   ...
    actions = [GeneratePDF]

Then it appeared with the description that I had placed in the class as a class attribute.

Into Django Admin

I could also reuse this class to create a URL and successfully generate the PDF.

# project/urls.py

from project.own_actions import GeneratePDF


urlpatterns = [
    path('admin/', admin.site.urls),
    ...
    path('export/', GeneratePDF, name="export-pdf")
]

PDF Generated

Statfarad answered 14/4, 2022 at 2:38 Comment(0)
C
2

You can still achieve the same goal without using the display decorator.

list_display = ('upper_case_name',)

def upper_case_name(self, obj):
    return "%s" % (obj.name.upper())
upper_case_name.short_description = "Name"
Chronaxie answered 9/12, 2021 at 18:23 Comment(0)
C
2

Using the answer from Jermaine, you can create your own decorator and monkey patch it to the admin module. This would allow you to use the @admin.display(description="...") decorator.

# Monkey patch admin to include the display decorator (available in future Django versions)
if not hasattr(admin, "display"):
    def display(description):
        def decorator(fn):
            fn.short_description = description
            return fn
        return decorator
    setattr(admin, "display", display)

Complete example:

from django.contrib import admin
from .models import Shop

# Monkey patch admin to include the display decorator (available in future Django versions)
if not hasattr(admin, "display"):
    def display(description):
        def decorator(fn):
            fn.short_description = description
            return fn
        return decorator
    setattr(admin, "display", display)


@admin.register(Shop)
class ShopAdmin(admin.ModelAdmin):

     @admin.display(description='Name')
     def upper_case_name(self,obj):
         return("%s" % (obj.name)).upper()  

This is not a super clean approach, but if you would like to use the decorator you can do it this way.

Clew answered 22/3, 2022 at 21:4 Comment(0)
H
1

In my case you should activate virtualenv , by typing "pipenv shell" command I think it helps you too

Hadst answered 27/10, 2021 at 3:13 Comment(0)
P
1

In my case, for some reason, django was downgraded. I ran "pipenv update django" and it worked.

Painkiller answered 8/11, 2021 at 4:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.