Skip to Content
FeaturesModulesDjango AdminField TypesAdvanced Fields

Advanced Field Types

Advanced field types for displaying complex status information, composite columns, counters, and conditional badges.

StackedField (Composite Column)

Combine multiple fields in one compact column. Perfect for reducing admin columns while keeping all important information visible.

Parameters

ParameterTypeDefaultDescription
namestrrequiredVirtual field name (used in list_display)
titlestr | NoneNoneColumn header title
rowslist[RowItem | list[RowItem]]requiredList of rows to display
gapstr"0.25rem"Gap between rows (CSS)
inline_gapstr"0.5rem"Gap between inline items (CSS)
align"left" | "center" | "right""left"Text alignment
min_widthstr | NoneNoneMinimum column width (CSS)
max_widthstr"300px"Maximum column width (CSS)

RowItem

Each row can be a single RowItem or a list of RowItem for inline (horizontal) display.

ParameterTypeDefaultDescription
fieldstrrequiredModel field name (supports __ for relations)
widgetstr"text"Widget type: text, badge, datetime_relative, money_field
boldboolFalseBold text
mutedboolFalseMuted/secondary text color
monospaceboolFalseMonospace font
prefixstr | NoneNoneText prefix
suffixstr | NoneNoneText suffix (e.g., " km", " beds")
truncateint | NoneNoneTruncate text to N characters
iconstr | NoneNoneMaterial icon name
label_mapdict | NoneNoneBadge variant mapping {value: "success"|"warning"|...}
true_labelstr | NoneNoneCustom label for True boolean badges
false_labelstr | NoneNoneCustom label for False boolean badges
hide_if_emptyboolTrueHide row if value is empty

Basic Usage

from django_cfg.modules.django_admin import StackedField, RowItem # Simple stacked info StackedField( name='vehicle_info', title='Vehicle', rows=[ RowItem(field='display_name', bold=True, truncate=30), RowItem(field='year'), RowItem(field='mileage', suffix=' km'), ], )

Examples

Property Listing

StackedField( name='property_info', title='Property', rows=[ RowItem(field='title', bold=True, truncate=35), [ # Inline row (horizontal) RowItem( field='listing_type', widget='badge', label_map={'rent': 'info', 'sale': 'success'}, ), RowItem(field='property_type__name', muted=True), ], RowItem(field='location__name', icon='location_on', muted=True), ], max_width='280px', )

Renders as:

Beautiful Villa in Seminyak ← Bold, truncated title [Rent] Apartment ← Badge + muted type inline 📍 Seminyak, Bali ← Icon + muted location

Vehicle with Specs

StackedField( name='vehicle_info', title='Vehicle', rows=[ RowItem(field='display_name', bold=True, truncate=30), [ # Specs inline RowItem(field='year'), RowItem(field='mileage', suffix=' km'), ], RowItem( field='fuel_type', widget='badge', label_map={ 'gasoline': 'info', 'diesel': 'warning', 'hybrid': 'success', 'electric': 'primary', }, ), ], max_width='250px', )

Pricing Info

StackedField( name='pricing_info', title='Price', rows=[ RowItem(field='price', widget='money_field', bold=True), [ RowItem(field='bedrooms', suffix=' bed'), RowItem(field='bathrooms', suffix=' bath'), ], ], )

Status with AI Badge

StackedField( name='status_info', title='Status', rows=[ [ RowItem( field='status', widget='badge', label_map={ 'active': 'success', 'inactive': 'secondary', 'sold': 'danger', }, ), RowItem( field='is_normalized', widget='badge', label_map={True: 'success'}, true_label='AI', # Shows "AI" instead of "True" ), ], ], )

Renders as: [Active] [AI] when status=active and is_normalized=True

Source Info

StackedField( name='source_info', title='Source', rows=[ RowItem(field='source__name', bold=True), RowItem(field='listing_id', monospace=True, muted=True, truncate=12), ], )

Reduces Columns Dramatically StackedField can reduce admin list from 15+ columns to 5-7 while keeping all information visible. Perfect for compact, scannable admin lists.

Virtual Field Name The name parameter is a virtual field name used in list_display. It doesn’t need to exist on the model - the actual data comes from RowItem.field values.

StatusBadgesField

Display multiple conditional badges based on model field values. Perfect for showing multiple status flags like permissions, features, or user attributes.

Parameters

ParameterTypeDefaultDescription
namestrrequiredField name
titlestr | NoneNoneDisplay title
badge_ruleslist[BadgeRule]requiredList of badge rules
empty_textstr | NoneNoneText when no badges match
empty_variantstr"secondary"Variant for empty badge
separatorstr" "Separator between badges

BadgeRule

ParameterTypeDefaultDescription
condition_fieldstrrequiredField to check (e.g., "is_bot")
condition_valueAnyTrueValue that triggers badge
labelstrrequiredBadge text
variantLiteral["primary", "secondary", "success", "danger", "warning", "info"]requiredBadge color variant
iconstr | NoneNoneMaterial icon name

Basic Usage

from django_cfg.modules.django_admin import StatusBadgesField, BadgeRule, Icons # User status badges StatusBadgesField( name="status_badges", title="Status", badge_rules=[ BadgeRule( condition_field="is_bot", condition_value=True, label="Bot", variant="info", icon=Icons.SMART_TOY ), BadgeRule( condition_field="is_verified", condition_value=True, label="Verified", variant="success", icon=Icons.VERIFIED ), BadgeRule( condition_field="is_premium", condition_value=True, label="Premium", variant="warning", icon=Icons.STAR ), ], empty_text="Regular", empty_variant="secondary" )

Examples

User Flags

display_fields=[ StatusBadgesField( name="user_flags", title="Flags", badge_rules=[ BadgeRule( condition_field="is_bot", condition_value=True, label="Bot", variant="info", icon=Icons.SMART_TOY ), BadgeRule( condition_field="is_verified", condition_value=True, label="Verified", variant="success", icon=Icons.VERIFIED ), BadgeRule( condition_field="is_scam", condition_value=True, label="Scam", variant="danger", icon=Icons.WARNING ), ], empty_text="None", ), ]

Renders as:

  • If is_bot=True and is_verified=True: Bot (blue) Verified (green)
  • If all flags are False: None (gray)

Permissions

display_fields=[ StatusBadgesField( name="permissions", title="Access", badge_rules=[ BadgeRule( condition_field="can_edit", condition_value=True, label="Edit", variant="primary", icon=Icons.EDIT ), BadgeRule( condition_field="can_delete", condition_value=True, label="Delete", variant="danger", icon=Icons.DELETE ), BadgeRule( condition_field="can_publish", condition_value=True, label="Publish", variant="success", icon=Icons.PUBLISH ), ], empty_text="Read Only", ), ]

Feature Flags

display_fields=[ StatusBadgesField( name="features", title="Features", badge_rules=[ BadgeRule( condition_field="feature_ai", condition_value=True, label="AI", variant="warning", icon=Icons.PSYCHOLOGY ), BadgeRule( condition_field="feature_api", condition_value=True, label="API", variant="info", icon=Icons.API ), BadgeRule( condition_field="feature_analytics", condition_value=True, label="Analytics", variant="success", icon=Icons.ANALYTICS ), ], ), ]

Replaces @computed_field StatusBadgesField eliminates the need for imperative @computed_field methods with conditional badge logic. Everything is declarative!

Flexible Conditions BadgeRule supports any field value, not just booleans. You can check string values, numbers, or any other type.

CounterBadgeField

Display a counter value as a badge with optional link. Perfect for showing counts like messages, views, or notifications with clickable links to filtered lists.

Parameters

ParameterTypeDefaultDescription
namestrrequiredField name
titlestr | NoneNoneDisplay title
count_fieldstrrequiredField containing the count
variantLiteral["primary", "secondary", "success", "danger", "warning", "info"]"primary"Badge color variant
iconstr | NoneNoneMaterial icon name
link_url_templatestr | NoneNoneURL with {obj.field} placeholders
link_targetstr"_self"Link target
format_thousandsboolTrueFormat with thousands separator
hide_on_zeroboolFalseHide when count is 0
empty_displayboolFalseShow dash when count is 0
empty_textstr"-"Text for zero count

Basic Usage

from django_cfg.modules.django_admin import CounterBadgeField, Icons # Messages count with link CounterBadgeField( name="messages_badge", title="Messages", count_field="messages_count", variant="primary", icon=Icons.MESSAGE, link_url_template="/admin/app/message/?user__id={obj.id}", hide_on_zero=True, ) # Simple counter without link CounterBadgeField( name="views", title="Views", count_field="view_count", variant="info", icon=Icons.VISIBILITY, )

Examples

display_fields=[ CounterBadgeField( name="messages_badge", title="Messages", count_field="messages_count", variant="primary", icon=Icons.MESSAGE, link_url_template="/admin/telegram_spy/telegrammessage/?sender__id__exact={obj.id}", hide_on_zero=True, format_thousands=True, ), ]

Renders as:

  • If count is 1,234: Clickable badge showing 1,234 with message icon
  • If count is 0: Nothing (hidden due to hide_on_zero=True)

Likes Counter

display_fields=[ CounterBadgeField( name="likes", title="Likes", count_field="like_count", variant="danger", icon=Icons.FAVORITE, empty_display=True, # Show "-" instead of hiding ), ]

Renders as:

  • If count is 42: Badge showing 42 with heart icon
  • If count is 0: Badge showing -

Error Counter

display_fields=[ CounterBadgeField( name="errors", title="Errors", count_field="error_count", variant="danger", icon=Icons.ERROR, link_url_template="/admin/app/log/?object_id={obj.id}&level=ERROR", hide_on_zero=True, ), ]

Automatic Formatting CounterBadgeField automatically:

  • Formats large numbers with thousands separator (1,234)
  • Makes the entire badge clickable if link_url_template is provided
  • Supports template variables like {obj.id}, {obj.username}, etc.
  • Hides or shows empty state based on configuration

Replaces format_html + Badge Previously you needed @computed_field with format_html() to wrap a badge in a link. Now it’s fully declarative!

ToonField

Renders JSON/dict fields as TOON (Token-Oriented Object Notation) with a JSON fallback. Provides a compact list preview and a full collapsible viewer on the change form.

The user’s preferred mode (TOON or JSON) is saved in localStorage and remembered across all admin pages.

Parameters

ParameterTypeDefaultDescription
namestrrequiredField name
titlestr | NoneNoneColumn header title
collapsibleboolTrueWrap the change-form viewer in <details>/<summary>
default_openboolFalseOpen the collapsible block by default
default_mode"toon" | "json""toon"Initial display mode (persisted in localStorage)
preview_linesint3Lines visible in list_display before expand (1–20)
labelstr | NoneNoneHeader label in <details> (falls back to titlename)
max_heightstr"24rem"CSS max-height of the content area

Basic Usage

from django_cfg.modules.django_admin import ToonField # In list_display — compact preview with expand/collapse ToonField(name='metadata') ToonField(name='settings', preview_lines=5) # In readonly_fields — full collapsible block with JSON↔TOON toggle ToonField( name='raw_data', collapsible=True, default_mode='toon', label='Request payload', max_height='32rem', )

Examples

API Request / Response Payload

display_fields=[ ToonField( name='request_payload', title='Request', collapsible=True, default_open=True, default_mode='toon', ), ToonField( name='response_payload', title='Response', collapsible=True, default_mode='json', ), ]

Compact Config Preview in List

config = AdminConfig( model=Integration, list_display=['name', 'status', 'config_preview'], display_fields=[ ToonField(name='config_preview', title='Config', preview_lines=4), ], )

Renders as: First 4 lines of the TOON representation with an expand button.

JSON Metadata with Custom Label

ToonField( name='extra_data', label='Extra metadata', collapsible=True, default_open=False, max_height='20rem', )

TOON Format TOON (Token-Oriented Object Notation) is an indentation-based, token-efficient alternative to JSON. It removes quotes, braces and colons — making deeply nested data much easier to scan visually.

Mode Persistence The viewer remembers the last chosen mode (TOON vs JSON) in localStorage under the key django_admin_toon_mode. One preference applies across all ToonField instances.


MarkdownField

Render markdown content with beautiful styling and optional collapsible UI.

Parameters

ParameterTypeDefaultDescription
namestrrequiredField name (or method name)
titlestr | NoneNoneDisplay title
collapsibleboolTrueWrap in collapsible details/summary
default_openboolFalseOpen by default if collapsible
max_heightstr | None"500px"Max height with scrolling
full_widthboolTrueSpan full width of fieldset (overrides Unfold max-width)
source_filestr | NoneNoneStatic file path (e.g., "docs/api.md")
source_fieldstr | NoneNoneAlternative field name for content
enable_pluginsboolTrueEnable mistune plugins (tables, etc.)
header_iconstr | NoneIcons.DESCRIPTIONMaterial icon for header

Basic Usage

from django_cfg.modules.django_admin import MarkdownField, Icons # From model field (markdown string) MarkdownField( name="description", title="Documentation", collapsible=True ) # From file path field MarkdownField( name="docs_path", title="User Guide", collapsible=True, default_open=True ) # Static file (same for all objects) MarkdownField( name="static_doc", # method that returns file path title="API Documentation", source_file="docs/api.md", # relative to app root max_height="500px" ) # Dynamic markdown with custom title MarkdownField( name="get_help_text", # method that generates markdown title="Help", collapsible=True, enable_plugins=True )

Examples

Simple Markdown

display_fields=[ MarkdownField( name="description", title="Documentation", collapsible=True, max_height="400px" ), ]

Renders as: Collapsible markdown content with syntax highlighting and styling

Static Documentation

# Add method to admin class @computed_field("API Docs") def api_documentation(self, obj): return "docs/api.md" # Returns file path # In config display_fields=[ MarkdownField( name="api_documentation", title="API Documentation", source_file="docs/api.md", default_open=True, ), ]

Renders as: Static markdown file shown for all objects

Dynamic Content

# Model method that generates markdown class MyModel(models.Model): def get_help_text(self): return f""" # Help for {self.name} - **Status**: {self.status} - **Created**: {self.created_at} ## Actions - Edit settings - View logs """ # In config display_fields=[ MarkdownField( name="get_help_text", title="Help", collapsible=True, enable_plugins=True ), ]

Renders as: Dynamically generated markdown for each object

Use Cases Perfect for:

  • Documentation - API docs, user guides, help text
  • Rich descriptions - Product descriptions with formatting
  • Change logs - Version history with markdown
  • Instructions - Step-by-step guides

Auto-Detection MarkdownField automatically detects whether the content is:

  • A file path (.md extension or exists as file) → loads and renders file
  • A markdown string → renders directly

Next Steps

Last updated on