django-unfold-4: @display Decorator
This article describes how to use the @display decorator in Django Unfold to provide data to components when displaying list data. Refer to the Django Unfold @display decorator documentation.
@display Decorator in Django
1. Basic Concept
The @display decorator can be applied to model class methods. These methods will appear as virtual fields in the Admin's list_display.
from django.contrib import admin
from django.contrib.admin.decorators import display
class Product(models.Model):
name = models.CharField(max_length=100)
price = models.DecimalField(max_digits=10, decimal_places=2)
stock = models.IntegerField()
@display(description='Stock Status')
def stock_status(self):
if self.stock > 50:
return 'Adequate'
elif self.stock > 10:
return 'Average'
else:
return 'Low'2. Registering in Admin
@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ['name', 'price', 'stock', 'stock_status']3. Key Parameters Explained
- description: Sets the column title.
- ordering: Sets the sort field.
- boolean: Whether to display as a boolean value.
@display(description='Price', ordering='price')
def formatted_price(self):
return f"¥{self.price}"
@display(description='In Stock', boolean=True)
def has_stock(self):
return self.stock > 04. Complex Business Logic
class Order(models.Model):
total_amount = models.DecimalField(max_digits=10, decimal_places=2)
status = models.CharField(max_length=20)
created_at = models.DateTimeField(auto_now_add=True)
@display(description='Order Status')
def order_status_with_color(self):
status_colors = {
'pending': 'orange',
'paid': 'green',
'shipped': 'blue',
'delivered': 'purple'
}
color = status_colors.get(self.status, 'gray')
return format_html(
'<span style="color: {};">{}</span>',
color, self.get_status_display()
)
@display(description='Amount Range')
def amount_range(self):
if self.total_amount < 100:
return 'Small'
elif self.total_amount < 1000:
return 'Medium'
else:
return 'Large'@display Decorator in Django Unfold
Unfold provides its own enhanced @display decorator. It works with Django's native decorator but adds more powerful customization features. It's mainly used to improve the display of fields in the admin changelist.
Two Main Enhanced Features
A. Label Display
With the label parameter, you can render field values as colored background labels (like badges). This is great for showing status information. Basic usage (label=True): Uses the default color scheme. Advanced usage (label={value: color}): Customize colors for different values. Supported colors:
- success (green): Success/completed status
- info (blue): Information/neutral status
- warning (orange): Warning status
- danger (red): Danger/error status
B. Two-Line Header Display
With the header=True parameter, you can show two lines of information in a single table cell. You can also add an avatar or icon prefix. Return value: You need to return a list/tuple with 2 or 3 elements. [First line main title, Second line subtitle, (optional) prefix info] Prefix info: Can be text (like name initials) or a dictionary with image path and style for displaying an avatar.
# admin.py
from django.db.models import TextChoices
from django.utils.translation import gettext_lazy as _
from unfold.admin import ModelAdmin
from unfold.decorators import display
class UserStatus(TextChoices):
ACTIVE = "ACTIVE", _("Active")
PENDING = "PENDING", _("Pending")
INACTIVE = "INACTIVE", _("Inactive")
CANCELLED = "CANCELLED", _("Cancelled")
class UserAdmin(ModelAdmin):
list_display = [
"display_as_two_line_heading",
"show_status",
"show_status_with_custom_label",
]
@display(
description=_('Status'),
ordering='status',
label=True
)
def show_status_default_color(self, obj): # Default color
return obj.status
@display(
description=_('Status'),
ordering='status',
label={
UserStatus.ACTIVE: "success", # green
UserStatus.PENDING: "info", # blue
UserStatus.INACTIVE: "warning", # orange
UserStatus.CANCELLED: "danger", # red
},
)
def show_status_customized_color(self, obj): # Custom color
return obj.status
@display(description=_('Status with label'), ordering='status', label=True)
def show_status_with_custom_label(self, obj): # Custom label
return obj.status, obj.get_status_display()
@display(header=True) # Two-line header
def display_as_two_line_heading(self, obj):
"""
Third argument is short text which will appear as prefix in circle
"""
return [
"First main heading",
"Smaller additional description", # Use None if you don't need it
"AB", # Short text that will appear in front
# Image instead of initials. Initials are ignored if image is available
{
"path": "some/path/picture.jpg",
"squared": True, # Picture displayed in square format
"borderless": True, # Picture displayed without border
"width": 64, # Removes default width
"height": 48, # Removes default height
}
]Dropdown Menu Support
This is a powerful feature. With the dropdown=True parameter, you can turn any field into a clickable dropdown menu. List dropdown: Provide an items list to generate a traditional menu item list. Each menu item can include a title and link. You can customize the menu's width, height, and style (like striped intervals). Parameters include:
- title (required) - Text that appears in the column header and triggers the dropdown
- items (required) - Array of menu items. Each item requires:
- title - Text label for the menu item
- link (optional) - URL or path for the item
- striped (optional) - Adds alternating background colors to list items
- height (optional) - Sets maximum height in pixels. Content becomes scrollable after this.
- width (optional) - Defines the dropdown's width in pixels
class UserAdmin(ModelAdmin):
list_display = [
"display_dropdown",
]
@display(description=_('Status'), dropdown=True)
def display_dropdown(self, obj):
return {
# Clickable title displayed in the column
"title": "Custom dropdown title",
# Striped design for items
"striped": True, # Optional
# Dropdown height. Scrollbar appears for longer content
"height": 200, # Optional
# Dropdown width
"width": 240, # Optional
"items": [
{
"title": "First title",
"link": "#" # Optional
},
{
"title": "Second title",
"link": "#" # Optional
},
]
}Custom template dropdown: With the content parameter, you can inject any custom HTML template content. This lets you create complex interactive dropdown interfaces (like charts, forms, etc.).
class UserAdmin(ModelAdmin):
list_display = [
"display_dropdown",
]
@display(description=_('Status'), dropdown=True)
def display_dropdown(self, obj):
return {
"title": "Custom dropdown title",
"content": "template content",
}Usage Tips
- Performance considerations: Avoid complex database queries in @display methods
- HTML security: Use format_html() to ensure HTML content is safe
- Translation support: Use gettext_lazy for description text
from django.utils.translation import gettext_lazy as _
@display(description=_('Product Status'))
def status_display(self):
return self.get_status_display()