Skip to Content

Real-World Examples

Complete working examples from production code showing best practices and common patterns.

Payment Admin

Complete payment management with transaction details, QR codes, and status tracking.

""" Payment Admin - Complete example from production Features: Badges, Currency, DateTime, Custom readonly fields with self.html """ from django.contrib import admin from django.utils import timezone from django_cfg.modules.django_admin import ( AdminConfig, BadgeField, CurrencyField, DateTimeField, FieldsetConfig, Icons, UserField, ) from django_cfg.modules.django_admin.base import PydanticAdmin # Declarative configuration payment_config = AdminConfig( model=Payment, # Performance optimization select_related=["user", "currency"], # List display list_display=[ "internal_payment_id", "user", "amount_usd", "currency", "status", "status_changed_at", "created_at" ], # Auto-generated display methods display_fields=[ BadgeField( name="internal_payment_id", title="Payment ID", variant="info", icon=Icons.RECEIPT ), UserField( name="user", title="User", header=True # Show with avatar ), CurrencyField( name="amount_usd", title="Amount", currency="USD", precision=2 ), BadgeField( name="status", title="Status", label_map={ "pending": "warning", "confirming": "info", "confirmed": "primary", "completed": "success", "partially_paid": "warning", "failed": "danger", "cancelled": "secondary", "expired": "danger" } ), DateTimeField( name="status_changed_at", title="Status Changed", ordering="status_changed_at" ), DateTimeField( name="created_at", title="Created", ordering="created_at" ), ], # Filters list_filter=["status", "currency", "created_at"], search_fields=["internal_payment_id", "user__username", "user__email"], # Readonly fields (custom methods below) readonly_fields=["payment_details_display", "qr_code_display"], # Fieldsets fieldsets=[ FieldsetConfig( title="Basic Information", fields=["id", "internal_payment_id", "user", "status"] ), FieldsetConfig( title="Payment Details", fields=["amount_usd", "currency", "pay_amount", "payment_details_display"] ), FieldsetConfig( title="QR Code", fields=["qr_code_display"], collapsed=True ), ], ) @admin.register(Payment) class PaymentAdmin(PydanticAdmin): """Enhanced payment admin with custom readonly fields.""" config = payment_config def payment_details_display(self, obj): """Detailed payment information using self.html utilities.""" if not obj.pk: return "Save to see details" details = [] # Payment age age = timezone.now() - obj.created_at age_text = f"{age.days} days, {age.seconds // 3600} hours" details.append(self.html.inline([ self.html.span("Age:", "font-semibold"), self.html.span(age_text, "") ], separator=" ")) # Transaction hash with explorer link if obj.transaction_hash: explorer_link = obj.get_explorer_link() if explorer_link: details.append(self.html.inline([ self.html.span("Transaction:", "font-semibold"), self.html.link(explorer_link, obj.transaction_hash[:16] + "...", target="_blank") ], separator=" ")) # Confirmations badge if obj.confirmations_count > 0: details.append(self.html.badge( f"{obj.confirmations_count} confirmations", variant="info", icon=Icons.CHECK_CIRCLE )) # Pay address (monospace code) if obj.pay_address: details.append(self.html.inline([ self.html.span("Pay Address:", "font-semibold"), self.html.span(f'<code>{obj.pay_address}</code>', "") ], separator=" ")) # Expiration status if obj.expires_at: if obj.is_expired: details.append(self.html.badge( f"Expired ({obj.expires_at})", variant="danger", icon=Icons.ERROR )) else: details.append(self.html.inline([ self.html.span("Expires At:", "font-semibold"), self.html.span(str(obj.expires_at), "") ], separator=" ")) return "<br>".join(details) payment_details_display.short_description = "Payment Details" def qr_code_display(self, obj): """QR code for payment address.""" if not obj.pay_address: return self.html.empty("No payment address") qr_url = f"/api/payments/{obj.id}/qr/" return f'<img src="{qr_url}" alt="QR Code" width="200" height="200">' qr_code_display.short_description = "QR Code"

User Admin

User management with computed fields, status badges, and related counts.

""" User Admin - Complete example with computed fields Features: @computed_field decorator, Status badges, Annotations """ from django.contrib import admin from django.db.models import Count from django_cfg.modules.django_admin import ( AdminConfig, BadgeField, DateTimeField, Icons, UserField, computed_field, annotated_field, ) from django_cfg.modules.django_admin.base import PydanticAdmin # Configuration with annotations customuser_config = AdminConfig( model=CustomUser, # Performance prefetch_related=["groups", "user_permissions"], # Annotations for counts annotations={ 'activity_count': Count('activities'), 'email_count': Count('email_logs'), 'ticket_count': Count('support_tickets'), }, # List display list_display=[ "avatar", "email", "full_name", "status", "activity_count_display", "last_login", "date_joined" ], # Auto-generated fields display_fields=[ UserField(name="avatar", title="Avatar", header=True), BadgeField(name="email", title="Email", variant="info", icon=Icons.EMAIL), DateTimeField(name="last_login", title="Last Login", ordering="last_login"), DateTimeField(name="date_joined", title="Joined", ordering="date_joined"), ], # Filters list_filter=["is_staff", "is_active", "date_joined"], search_fields=["email", "first_name", "last_name"], ) @admin.register(CustomUser) class UserAdmin(PydanticAdmin): """Enhanced user admin with computed fields.""" config = customuser_config @computed_field("Full Name", ordering="last_name") def full_name(self, obj): """Display full name with badge.""" name = obj.get_full_name() if not name: return self.html.badge("No name", variant="secondary", icon=Icons.PERSON) return self.html.badge(name, variant="primary", icon=Icons.PERSON) @computed_field("Status", ordering="is_active") def status(self, obj): """Enhanced status display with conditional icons and colors.""" if obj.is_superuser: return self.html.badge( "Superuser", variant="danger", icon=Icons.ADMIN_PANEL_SETTINGS ) elif obj.is_staff: return self.html.badge( "Staff", variant="warning", icon=Icons.SETTINGS ) elif obj.is_active: return self.html.badge( "Active", variant="success", icon=Icons.CHECK_CIRCLE ) else: return self.html.badge( "Inactive", variant="secondary", icon=Icons.CANCEL ) @annotated_field("Activities", annotation_name="activity_count") def activity_count_display(self, obj): """Display activity count from annotation.""" count = getattr(obj, 'activity_count', 0) if count == 0: return self.html.empty() return self.html.badge( f"{count} activit{'ies' if count != 1 else 'y'}", variant="info", icon=Icons.HISTORY )

E-commerce Order Admin

Order management with multiple related fields and aggregations.

""" Order Admin - E-commerce example Features: Multi-field display, Aggregations, Related data """ from django.contrib import admin from django.db.models import Count, Sum from django_cfg.modules.django_admin import ( AdminConfig, BadgeField, CurrencyField, DateTimeField, FieldsetConfig, Icons, UserField, annotated_field, ) from django_cfg.modules.django_admin.base import PydanticAdmin order_config = AdminConfig( model=Order, # Performance select_related=["user", "shipping_address"], prefetch_related=["items"], # Annotations annotations={ 'items_count': Count('items'), 'total_amount': Sum('items__price'), }, # List display list_display=[ "order_number", "user", "items_count_display", "total_amount_display", "status", "created_at" ], # Display fields display_fields=[ UserField(name="user", header=True, ordering="user__username"), BadgeField( name="status", label_map={ "pending": "warning", "processing": "info", "shipped": "primary", "delivered": "success", "cancelled": "danger", }, icon=Icons.SHOPPING_CART ), DateTimeField(name="created_at", ordering="created_at"), ], # Fieldsets fieldsets=[ FieldsetConfig( title="Order Information", fields=["order_number", "user", "status"] ), FieldsetConfig( title="Shipping", fields=["shipping_address", "shipping_method", "tracking_number"] ), FieldsetConfig( title="Items", fields=["order_items_display"], collapsed=True ), ], readonly_fields=["order_items_display"], list_filter=["status", "created_at"], search_fields=["order_number", "user__email"], ) @admin.register(Order) class OrderAdmin(PydanticAdmin): """E-commerce order admin.""" config = order_config @annotated_field("Items", annotation_name="items_count") def items_count_display(self, obj): """Display item count from annotation.""" count = getattr(obj, 'items_count', 0) return self.html.badge( f"{count} item{'s' if count != 1 else ''}", variant="info", icon=Icons.SHOPPING_BAG ) @annotated_field("Total", annotation_name="total_amount", ordering="total_amount") def total_amount_display(self, obj): """Display total from annotation.""" from django_cfg.modules.django_admin.utils.displays import MoneyDisplay from django_cfg.modules.django_admin.models.display_models import MoneyDisplayConfig total = getattr(obj, 'total_amount', 0) or 0 config = MoneyDisplayConfig(currency="USD", precision=2) return MoneyDisplay.amount(total, config) def order_items_display(self, obj): """Display order items with self.html.""" if not obj.pk: return "Save to see items" items = obj.items.all() if not items: return self.html.empty("No items") item_lines = [] for item in items: item_lines.append(self.html.inline([ self.html.badge(item.product.name, variant="primary"), self.html.span(f"Ă—{item.quantity}", ""), self.html.span(f"${item.price:.2f}", "font-mono"), ], separator=" ")) return "<br>".join(item_lines) order_items_display.short_description = "Order Items"

Newsletter Admin

Newsletter management with email tracking and status indicators.

""" Newsletter Admin - Email campaign example Features: Email tracking, Custom filters, Multiple computed fields """ from django.contrib import admin from django_cfg.modules.django_admin import ( AdminConfig, BadgeField, DateTimeField, Icons, UserField, computed_field, ) from django_cfg.modules.django_admin.base import PydanticAdmin emaillog_config = AdminConfig( model=EmailLog, # Performance select_related=['user', 'newsletter'], # List display list_display=[ "user_display", "recipient_display", "subject_display", "status_display", "tracking_display", "created_at" ], # Display fields display_fields=[ UserField(name="user", title="User"), BadgeField(name="recipient", title="Recipient", variant="info", icon=Icons.EMAIL), BadgeField(name="subject", title="Subject", variant="primary"), DateTimeField(name="created_at", title="Created", ordering="created_at"), ], # Filters list_filter=["status", "created_at", "newsletter"], search_fields=["recipient", "subject", "user__username"], ) @admin.register(EmailLog) class EmailLogAdmin(PydanticAdmin): """Newsletter email tracking admin.""" config = emaillog_config @computed_field("Status", ordering="status") def status_display(self, obj): """Status with icons.""" icon_map = { 'pending': Icons.SCHEDULE, 'sent': Icons.CHECK_CIRCLE, 'failed': Icons.ERROR, } variant_map = { 'pending': 'warning', 'sent': 'success', 'failed': 'danger', } return self.html.badge( obj.get_status_display(), variant=variant_map.get(obj.status, 'secondary'), icon=icon_map.get(obj.status) ) @computed_field("Tracking") def tracking_display(self, obj): """Email tracking indicators.""" badges = [] if obj.is_opened: badges.append(self.html.badge( "Opened", variant="success", icon=Icons.VISIBILITY )) if obj.is_clicked: badges.append(self.html.badge( "Clicked", variant="info", icon=Icons.MOUSE )) if not badges: return self.html.empty("No activity") return self.html.inline(badges, separator=" ")

Cryptocurrency Exchange Admin

Crypto exchange with smart decimal formatting and market data.

""" Exchange Admin - Cryptocurrency example Features: Smart decimal places, Large numbers, External links """ from django.contrib import admin from django_cfg.modules.django_admin import ( AdminConfig, BadgeField, BooleanField, CurrencyField, FieldsetConfig, Icons, ) from django_cfg.modules.django_admin.base import PydanticAdmin exchange_config = AdminConfig( model=Exchange, # List display list_display=[ "name", "code", "volume_24h_usd", "rank", "is_active", "is_verified" ], # Display fields display_fields=[ BadgeField(name="name", title="Exchange", variant="primary", icon=Icons.BUSINESS), BadgeField(name="code", title="Code", variant="info"), CurrencyField( name="volume_24h_usd", title="24h Volume", currency="USD", precision=0 # No decimals for large numbers ), BooleanField(name="is_active", title="Active"), BooleanField(name="is_verified", title="Verified"), ], # Fieldsets fieldsets=[ FieldsetConfig( title="Basic Information", fields=["name", "code", "description"] ), FieldsetConfig( title="Trading Data", fields=["volume_24h_usd", "rank", "trading_pairs_count"] ), FieldsetConfig( title="Links", fields=["website_link_display", "api_link_display"], collapsed=True ), ], readonly_fields=["website_link_display", "api_link_display"], list_filter=["is_active", "is_verified"], search_fields=["name", "code"], prepopulated_fields={'slug': ('name',)}, ) @admin.register(Exchange) class ExchangeAdmin(PydanticAdmin): """Cryptocurrency exchange admin.""" config = exchange_config def website_link_display(self, obj): """External website link.""" if not obj.website_url: return self.html.empty() return self.html.link( obj.website_url, "Visit Website", css_class="text-blue-600", target="_blank" ) website_link_display.short_description = "Website" def api_link_display(self, obj): """API documentation link.""" if not obj.api_url: return self.html.empty() return self.html.inline([ self.html.icon(Icons.API, size="sm"), self.html.link(obj.api_url, "API Docs", target="_blank") ]) api_link_display.short_description = "API"

Wallet Balance Admin

Exchange wallet balances with UUID shortening and custom avatar displays.

""" Wallet Balance Admin - Cryptocurrency exchange example Features: UUID shortening, custom avatars, balance displays, SafeString HTML """ from decimal import Decimal from django.contrib import admin from django_cfg.modules.django_admin import ( AdminConfig, DateTimeField, FieldsetConfig, Icons, computed_field, ) from django_cfg.modules.django_admin.base import PydanticAdmin wallet_config = AdminConfig( model=MarketAccountWallet, # Performance optimization select_related=[ 'account', 'account__account', 'account__account__exchange', 'account__account__user', 'coin' ], # List display with custom fields list_display=[ 'id_display', # Shortened UUID 'wallet_display', # Custom avatar + text 'coin_display', # Badge 'balance_total_display', 'balance_available_display', 'balance_locked_display', 'value_usd_display', 'last_sync_display', ], # Filters list_filter=[ 'account__market_type', 'account__account__exchange', 'coin__is_stable', 'account__is_active', ], # Search search_fields=[ 'id', 'coin__symbol', 'coin__label', 'account__account__name', 'account__account__user__email', ], # Ordering by balance ordering=['-balance_total', 'coin__symbol'], list_per_page=50, readonly_fields=['id', 'created_at', 'updated_at', 'last_sync_at'], ) @admin.register(MarketAccountWallet) class MarketAccountWalletAdmin(PydanticAdmin): config = wallet_config @computed_field("ID") def id_display(self, obj): """Short UUID with tooltip.""" return self.html.uuid_short(obj.id, length=6) @computed_field("Wallet") def wallet_display(self, obj): """Custom avatar with account info.""" if not obj.account or not obj.account.account: return self.html.empty("Unknown") account = obj.account.account market_type = obj.account.market_type header_text = f"{account.name} ({market_type})" subtitle_text = f"{account.exchange.name} - {account.user.email}" initials = account.name[:2].upper() # Use inline styles for perfect circular avatar from django.utils.safestring import mark_safe return mark_safe( f'<div class="flex items-center gap-3">' f'<div class="flex items-center justify-center rounded-full ' f'bg-primary-100 text-primary-600 font-semibold flex-shrink-0" ' f'style="width: 40px; height: 40px; min-width: 40px; min-height: 40px;">' f'{initials}' f'</div>' f'<div class="flex-1 min-w-0">' f'<div class="font-medium truncate">{header_text}</div>' f'<div class="text-sm text-gray-500 truncate">{subtitle_text}</div>' f'</div>' f'</div>' ) @computed_field("Coin") def coin_display(self, obj): """Coin badge.""" if not obj.coin: return self.html.empty("Unknown") return self.html.badge(obj.coin.symbol, variant="info", icon=Icons.CURRENCY_BITCOIN) @computed_field("Total") def balance_total_display(self, obj): """Total balance with currency.""" if obj.balance_total <= 0: return self.html.span("0.00", "text-gray-500") precision = obj.coin.precision_display if obj.coin else 8 formatted = f"{obj.balance_total:.{precision}f}".rstrip('0').rstrip('.') symbol = obj.coin.symbol if obj.coin else '' # Use inline with empty separator for adjacent HTML return self.html.inline([ f'<strong>{formatted}</strong>', f'<span class="text-gray-500 ml-1">{symbol}</span>', ], separator="") @computed_field("Available") def balance_available_display(self, obj): """Available balance.""" if obj.balance_available <= 0: return self.html.span("0.00", "text-gray-500") precision = obj.coin.precision_display if obj.coin else 8 formatted = f"{obj.balance_available:.{precision}f}".rstrip('0').rstrip('.') symbol = obj.coin.symbol if obj.coin else '' return self.html.inline([ self.html.span(formatted, "text-green-600 font-semibold"), f'<span class="text-gray-500 ml-1">{symbol}</span>', ], separator="") @computed_field("Value USD") def value_usd_display(self, obj): """USD value badge.""" if not obj.coin or not obj.coin.price_usd: return self.html.span("—", "text-gray-500") value_usd = obj.balance_total * obj.coin.price_usd if value_usd < Decimal('0.01'): return self.html.span("< $0.01", "text-gray-500") return self.html.badge( f"${value_usd:,.2f}", variant="success", icon=Icons.ATTACH_MONEY, )

Key Features:

  • uuid_short() for clean ID display with tooltip
  • Custom circular avatar with fixed proportions
  • inline() with separator="" for adjacent HTML elements
  • Tailwind CSS classes for styling
  • mark_safe() for complex HTML structures
  • Perfect for financial/crypto applications

Common Patterns

Pattern 1: Conditional Badges

@computed_field("Priority") def priority_display(self, obj): """Priority with colors based on value.""" priority_map = { 'low': ('info', Icons.ARROW_DOWNWARD), 'medium': ('warning', Icons.REMOVE), 'high': ('danger', Icons.ARROW_UPWARD), 'critical': ('danger', Icons.PRIORITY_HIGH), } variant, icon = priority_map.get(obj.priority, ('secondary', None)) return self.html.badge(obj.get_priority_display(), variant=variant, icon=icon)

Pattern 2: Multiple Inline Badges

@computed_field("Features") def features_display(self, obj): """Show multiple feature badges.""" badges = [] if obj.is_featured: badges.append(self.html.badge("Featured", variant="success", icon=Icons.STAR)) if obj.is_new: badges.append(self.html.badge("New", variant="info", icon=Icons.NEW_RELEASES)) if obj.on_sale: badges.append(self.html.badge("Sale", variant="danger", icon=Icons.LOCAL_OFFER)) return self.html.inline(badges, separator=" ") if badges else self.html.empty()

Pattern 3: Complex Readonly Field

def detailed_info_display(self, obj): """Complex info using multiple self.html methods.""" sections = [] # Header sections.append(self.html.span("Details", "text-lg font-bold")) # Key-value pairs info_items = [ ("ID", obj.id), ("Created", obj.created_at.strftime("%Y-%m-%d")), ("Updated", obj.updated_at.strftime("%Y-%m-%d")), ] for label, value in info_items: sections.append(self.html.inline([ self.html.span(f"{label}:", "font-semibold"), self.html.span(str(value), "") ], separator=" ")) # Status badge sections.append(self.html.badge( obj.get_status_display(), variant="success" if obj.is_active else "secondary" )) return "<br>".join(sections)

Time-Based Colored Text

@computed_field("Last Activity") def last_activity_display(self, obj): """Show time ago with color based on freshness.""" if not obj.last_activity_at: return self.html.empty("Never") from django.utils import timezone from django.utils.timesince import timesince age_seconds = (timezone.now() - obj.last_activity_at).total_seconds() time_ago = timesince(obj.last_activity_at, timezone.now()) # Color based on freshness if age_seconds < 120: # < 2 minutes color = "success" elif age_seconds < 300: # < 5 minutes color = "warning" else: # > 5 minutes color = "danger" return self.html.colored_text(f"{time_ago} ago", color)

Status with Icon and Color

@computed_field("Connection Status") def connection_status_display(self, obj): """Show connection status with icon and color.""" if obj.is_connected: return self.html.icon_text( Icons.CHECK_CIRCLE, "Connected", color="success" ) # Show disconnection time with color if obj.last_seen_at: from django.utils import timezone from django.utils.timesince import timesince age_seconds = (timezone.now() - obj.last_seen_at).total_seconds() time_ago = timesince(obj.last_seen_at, timezone.now()) # Gradually change color as time passes if age_seconds < 60: # < 1 minute color = "warning" icon = Icons.SCHEDULE elif age_seconds < 300: # < 5 minutes color = "warning" icon = Icons.CANCEL else: # > 5 minutes color = "danger" icon = Icons.ERROR return self.html.icon_text(icon, f"Offline {time_ago}", color=color) return self.html.icon_text(Icons.CANCEL, "Never connected", color="secondary")

Bot/API Configuration Admin

Complete example with JSON Widget for managing bot configurations and API schemas.

""" Bot Configuration Admin - JSON Widget example Features: JSON editor with copy button, Read-only schemas, Multiple JSON fields """ from django.contrib import admin from django_cfg.modules.django_admin import ( AdminConfig, BadgeField, BooleanField, DateTimeField, FieldsetConfig, Icons, JSONWidgetConfig, ShortUUIDField, ) from django_cfg.modules.django_admin.base import PydanticAdmin # Model class Bot(models.Model): id = models.UUIDField(primary_key=True, default=uuid.uuid4) name = models.CharField(max_length=100) adapter = models.ForeignKey(Adapter, on_delete=models.CASCADE) status = models.CharField(max_length=20, choices=STATUS_CHOICES) enabled = models.BooleanField(default=False) # JSON fields settings = models.JSONField(default=dict) # Editable configuration config_schema = models.JSONField(null=True) # Read-only Pydantic schema # Monitoring last_heartbeat_at = models.DateTimeField(null=True) is_alive = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) updated_at = models.DateTimeField(auto_now=True) # Declarative configuration bot_config = AdminConfig( model=Bot, # List display list_display=[ "id", "name", "adapter_display", "status_display", "enabled", "is_alive_display", "last_heartbeat_display", "created_at", ], # Auto-generated display methods display_fields=[ ShortUUIDField( name="id", title="ID", length=8, ), BadgeField( name="status", title="Status", label_map={ "stopped": "secondary", "starting": "info", "running": "success", "paused": "warning", "stopping": "warning", "error": "danger", }, icon=Icons.PLAY_CIRCLE, ), BooleanField(name="enabled", title="Enabled"), BooleanField(name="is_alive", title="Alive"), DateTimeField(name="created_at", title="Created", show_relative=True), DateTimeField(name="last_heartbeat_at", title="Last Heartbeat", show_relative=True), ], # JSON Widget configurations widgets=[ # Editable settings - tree mode JSONWidgetConfig( field="settings", mode="tree", # Interactive tree for editing height="400px", show_copy_button=True, ), # Read-only schema - view mode JSONWidgetConfig( field="config_schema", mode="view", # Read-only for Pydantic schema height="500px", # Larger height for schema show_copy_button=True, # Easy to copy schema ), ], # Fieldsets fieldsets=[ FieldsetConfig( title="Basic Info", fields=["id", "name", "description", "adapter"], ), FieldsetConfig( title="Runtime State", fields=["status", "enabled"], ), FieldsetConfig( title="Monitoring", fields=["last_heartbeat_at", "last_trade_at", "is_alive", "heartbeat_age_seconds"], ), FieldsetConfig( title="Settings", fields=["settings"], # Uses tree mode from widgets collapsed=True, ), FieldsetConfig( title="Config Schema", fields=[ "config_schema", # Uses view mode from widgets "config_schema_requested_display", ], collapsed=True, description="Pydantic JSON Schema from bot for dynamic UI generation", ), FieldsetConfig( title="Timestamps", fields=["created_at", "updated_at"], collapsed=True, ), ], # Configuration list_filter=["status", "enabled", "adapter"], search_fields=["name", "description", "id"], readonly_fields=[ "id", "created_at", "updated_at", "last_heartbeat_at", "is_alive", "config_schema", "config_schema_requested_display" ], ) # Register admin @admin.register(Bot) class BotAdmin(PydanticAdmin): """Admin interface for bots (unified runtime state)""" config = bot_config # Custom computed fields @computed_field("Adapter") def adapter_display(self, obj): """Display adapter with icon.""" if obj.adapter: return self.html.icon_text(Icons.EXTENSION, obj.adapter.name) return self.html.empty("-") @computed_field("Status") def status_display(self, obj): """Display status with icon.""" status_icons = { Bot.Status.STOPPED: Icons.STOP, Bot.Status.STARTING: Icons.PLAY_ARROW, Bot.Status.RUNNING: Icons.PLAY_CIRCLE, Bot.Status.PAUSED: Icons.PAUSE, Bot.Status.STOPPING: Icons.STOP_CIRCLE, Bot.Status.ERROR: Icons.ERROR, } return self.html.icon_text( status_icons.get(obj.status, Icons.HELP), obj.get_status_display(), ) @computed_field("Alive") def is_alive_display(self, obj): """Display if bot is alive.""" if obj.is_alive: return self.html.icon_text(Icons.CHECK_CIRCLE, "Yes", color="success") return self.html.icon_text(Icons.CANCEL, "No", color="secondary") @computed_field("Last Heartbeat") def last_heartbeat_display(self, obj): """Display last heartbeat with relative time.""" if obj.last_heartbeat_at: from django.utils import timezone from django.utils.timesince import timesince age_seconds = (timezone.now() - obj.last_heartbeat_at).total_seconds() if age_seconds < 120: # < 2 minutes color = "success" elif age_seconds < 300: # < 5 minutes color = "warning" else: color = "danger" time_ago = timesince(obj.last_heartbeat_at, timezone.now()) return self.html.colored_text(f"{time_ago} ago", color) return self.html.empty("-") @computed_field("Schema Requested") def config_schema_requested_display(self, obj): """Display when schema was requested.""" if obj.config_schema_requested_at: from django.utils import timezone from django.utils.timesince import timesince time_ago = timesince(obj.config_schema_requested_at, timezone.now()) return self.html.colored_text(f"{time_ago} ago", "info") return self.html.empty("-")

Key Features

JSON Widget Configuration:

  • settings field uses mode="tree" for interactive editing
  • config_schema field uses mode="view" for read-only display
  • Both have show_copy_button=True for easy copying
  • Different heights optimized for content

Centralized Widget Config:

widgets=[ JSONWidgetConfig( field="settings", mode="tree", # Editable height="400px", ), JSONWidgetConfig( field="config_schema", mode="view", # Read-only height="500px", ), ]

Clean Fieldsets:

# Just field names - widget config is separate! FieldsetConfig( title="Settings", fields=["settings"], # Widget config applied automatically )

Next Steps

Pro Tip All examples above are from real production code. Copy-paste and adapt them to your needs!