Skip to Content

Display Field Types

Display-focused field types for rich visual presentations in Django Admin, including user avatars, profile displays, external links, images, and image previews with modal zoom.

Overview

Display field types specialize in presenting visual and interactive content:

Field TypePurposeCommon Use Cases
AvatarFieldUser avatars with initials fallbackProfile pictures, user identity
UserFieldUser display with avatarAuthors, assignees, owners
LinkFieldText with external link and subtitleUsernames, titles, external resources
ImageFieldImages with captionsQR codes, photos, avatars
ImagePreviewFieldImage preview with modal zoom/panPhotos, thumbnails, media galleries
VideoFieldVideo thumbnail with platform detectionYouTube, Vimeo, direct videos

AvatarField

Display user avatars with automatic fallback to colored initials badge.

Parameters

class AvatarField(FieldConfig): name: str # Field name title: str | None = None # Display title photo_field: str # Model field for photo/avatar image name_field: str # Model field for display name initials_field: str # Field for extracting initials subtitle_field: str | None = None # Optional subtitle (email, user_id) avatar_size: int = 40 # Avatar size in pixels show_as_card: bool = False # Show as user card layout variant_field: str | None = None # Field to determine badge variant variant_map: dict[str, str] | None = None # Map field values to badge variants default_variant: str = "secondary" # Default badge variant initials_max_length: int = 2 # Maximum initials (1-3) empty_value: str = "—" # Value when None ordering: str | None = None # Sort field

Basic Usage

from django_cfg.modules.django_admin import AvatarField # Basic avatar - IMPORTANT: name must be a real model field! AvatarField( name="first_name", # Real model field, not virtual photo_field="photo_file", name_field="display_name", initials_field="first_name", ) # Avatar with subtitle AvatarField( name="username", # Real model field photo_field="avatar", name_field="full_name", initials_field="username", subtitle_field="email", show_as_card=True, ) # Avatar with custom sizing and variant mapping AvatarField( name="first_name", # Real model field photo_field="profile_picture", name_field="name", initials_field="first_name", avatar_size=48, variant_field="user_type", variant_map={ "admin": "success", "user": "secondary", "bot": "info", }, )

Examples

Basic Avatar

display_fields=[ AvatarField( name="username", # Real model field photo_field="photo", name_field="username", initials_field="first_name", ), ]

Renders as:

  • Photo if available
  • Colored badge with initials if no photo

User Card

display_fields=[ AvatarField( name="first_name", # Real model field photo_field="avatar", name_field="full_name", initials_field="first_name", subtitle_field="email", show_as_card=True, avatar_size=48, ), ]

Renders as: User card with avatar/initials, name, and email

Dynamic Colors

display_fields=[ AvatarField( name="username", # Real model field photo_field="photo", name_field="name", initials_field="username", variant_field="role", variant_map={ "admin": "success", "moderator": "primary", "user": "secondary", "bot": "info", }, default_variant="secondary", ), ]

Renders as: Initials badge color changes based on user role

IMPORTANT: Use Real Model Fields The name parameter MUST be a real model field, not a virtual field or @property!

❌ Wrong:

AvatarField( name="user_avatar", # Virtual field - doesn't exist in model ... )

✅ Correct:

AvatarField( name="first_name", # Real CharField in model photo_field="photo_file", name_field="display_name", # Can be @property initials_field="first_name", ... )

The name field is used by Django’s queryset system. Other fields like name_field, initials_field, subtitle_field can be properties or methods.

Automatic Fallback AvatarField automatically:

  • Shows photo if photo_field has value
  • Falls back to colored initials badge if no photo
  • Extracts initials from initials_field (e.g., “John Doe” → “JD”)
  • Uses variant mapping for dynamic badge colors

Use Cases Perfect for:

  • User lists (authors, assignees, owners)
  • Profile displays
  • Activity feeds
  • Comment sections
  • Any user-related display where visual identity matters

UserField

Display user information with avatar and optional header styling.

Parameters

class UserField(FieldConfig): name: str # Field name (must be ForeignKey to User) title: str | None = None # Display title header: bool = False # Show with avatar empty_value: str = "—" # Value when None ordering: str | None = None # Sort field (e.g., "user__username")

Basic Usage

from django_cfg.modules.django_admin import UserField # Simple user display UserField( name="author", title="Author", ordering="author__username", ) # User with avatar header UserField( name="owner", title="Owner", header=True, ordering="owner__username", )

Examples

Basic User

display_fields=[ UserField( name="author", title="Author", ordering="author__username", ), ]

Renders as: Username only

User with Avatar

display_fields=[ UserField( name="owner", title="Owner", header=True, ordering="owner__username", ), ]

Renders as: Avatar + Username in header style

Multiple Users

display_fields=[ UserField(name="created_by", title="Created By", header=True), UserField(name="assigned_to", title="Assigned To"), UserField(name="approved_by", title="Approved By"), ]

Requires User Relation UserField only works with ForeignKey fields pointing to the User model. Ensure select_related includes the user field for optimal performance.

LinkField

Display text as clickable link with optional icon and subtitle.

Parameters

class LinkField(FieldConfig): name: str # Field name (text to display) title: str | None = None # Display title link_field: str # Model field containing the URL link_icon: str | None = None # Material icon to display next to link link_target: str = "_blank" # Link target (_blank, _self, etc.) subtitle_field: str | None = None # Single field for subtitle subtitle_fields: list[str] | None = None # Multiple fields for subtitle subtitle_template: str | None = None # Template with {field_name} placeholders subtitle_separator: str = " • " # Separator for multiple fields subtitle_css_class: str = "text-sm text-gray-500" # CSS for subtitle empty_value: str = "—" # Value when None ordering: str | None = None # Sort field

Basic Usage

from django_cfg.modules.django_admin import LinkField, Icons # Basic link LinkField( name="username", title="Username", link_field="profile_url", ) # Link with icon LinkField( name="display_name", title="User", link_field="telegram_link", link_icon=Icons.OPEN_IN_NEW, ) # Link with subtitle from single field LinkField( name="title", title="Article", link_field="url", link_icon=Icons.ARTICLE, subtitle_field="author", ) # Link with subtitle from multiple fields LinkField( name="display_name", title="User Info", link_field="telegram_link", link_icon=Icons.OPEN_IN_NEW, subtitle_fields=["full_name", "phone"], ordering="username", )

Examples

display_fields=[ LinkField( name="website_url", title="Website", link_field="website_url", # Can link to itself link_icon=Icons.OPEN_IN_NEW, ), ]

Renders as:

https://example.com 🔗

Telegram User

# Real-world example: Telegram user with link # NOTE: display_name is a @property, use a real model field instead! display_fields=[ LinkField( name="username", # Real model field title="User Info", link_field="telegram_link", link_icon=Icons.OPEN_IN_NEW, subtitle_fields=["full_name", "phone"], subtitle_separator=" • ", ordering="username", ), ]

Renders as:

@username 🔗 John Doe • +1234567890

Template Subtitle

display_fields=[ LinkField( name="channel_title", title="Channel", link_field="channel_url", link_icon=Icons.OPEN_IN_NEW, subtitle_template="ID: {channel_id} • Subscribers: {subscriber_count}", ), ]

Renders as:

My Channel 🔗 ID: 123456 • Subscribers: 1,234

Subtitle Options

LinkField supports three mutually exclusive subtitle options:

# Option 1: Single field LinkField( name="username", link_field="profile_url", subtitle_field="email", # Shows email below username ) # Option 2: Multiple fields with separator LinkField( name="display_name", link_field="telegram_link", subtitle_fields=["full_name", "phone", "email"], subtitle_separator=" • ", # Joins with " • " ) # Option 3: Template with placeholders LinkField( name="product_name", link_field="product_url", subtitle_template="Price: ${price} | Stock: {stock_count}", )

Use Cases

LinkField is perfect for:

  • External links: Links to external websites, profiles, resources
  • User profiles: Telegram users, social media profiles
  • Resources: Articles, documents, API endpoints
  • Channel/Group links: Telegram channels, Discord servers

Real-World Example

from django_cfg.modules.django_admin import AdminConfig, LinkField, Icons telegram_user_config = AdminConfig( model=TelegramUser, list_display=["first_name", "username", "status_badges", "messages_badge"], display_fields=[ LinkField( name="username", # Real model field title="User Info", link_field="telegram_link", link_icon=Icons.OPEN_IN_NEW, subtitle_fields=["full_name", "phone"], ordering="username", ), ], )

Declarative Approach LinkField eliminates the need for @computed_field with raw HTML. Everything is configured declaratively with type-safe parameters.

No Raw HTML Required Unlike manual @computed_field implementations, LinkField handles all HTML generation internally. You just configure the fields and it renders properly.

ImageField

Display images from URLs with optional captions and styling.

Parameters

class ImageField(FieldConfig): name: str # Field name (or method name) title: str | None = None # Display title width: str | None = None # Image width (e.g., "200px") height: str | None = None # Image height (e.g., "200px") max_width: str = "200px" # Maximum width max_height: str | None = None # Maximum height border_radius: str | None = None # Border radius (e.g., "50%", "8px") caption: str | None = None # Static caption text caption_field: str | None = None # Model field to use as caption caption_template: str | None = None # Template with {field_name} placeholders alt_text: str = "Image" # Alt text for image empty_value: str = "—" # Value when None

Basic Usage

from django_cfg.modules.django_admin import ImageField # Simple image ImageField( name="photo_url", title="Photo", max_width="200px", ) # Image with static caption ImageField( name="thumbnail", title="Thumbnail", max_width="100px", caption="Product Image", ) # Image with caption from field ImageField( name="avatar_url", title="Avatar", width="50px", height="50px", border_radius="50%", caption_field="username", )

Examples

QR Code

# QR code with template caption display_fields=[ ImageField( name="get_qr_code_url", # Can be a method title="QR Code", max_width="200px", caption_template="Scan to pay: <code>{pay_address}</code>", ), ]

Result:

<img src="https://api.qrserver.com/..." alt="Image" style="max-width: 200px;"> <br><small>Scan to pay: <code>0x1234...5678</code></small>

Circular Avatar

# Circular avatar with username display_fields=[ ImageField( name="profile_picture", title="Avatar", width="50px", height="50px", border_radius="50%", caption_field="full_name", ), ]

Result:

<img src="/media/avatars/john.jpg" alt="Image" style="width: 50px; height: 50px; border-radius: 50%;"> <br><small>John Doe</small>

Product Photo

# Product photo with multiple fields in caption display_fields=[ ImageField( name="image_url", title="Product", max_width="150px", max_height="150px", caption_template="{name} - ${price}", ), ]

Result:

<img src="/media/products/laptop.jpg" alt="Image" style="max-width: 150px; max-height: 150px;"> <br><small>Laptop Pro - $1299</small>

Method Support

ImageField supports both model fields and methods:

class Payment(models.Model): pay_address = models.CharField(max_length=100) def get_qr_code_url(self, size=200): """Generate QR code URL dynamically.""" from urllib.parse import quote data = quote(self.pay_address) return f"https://api.qrserver.com/v1/create-qr-code/?size={size}x{size}&data={data}" # Use method in ImageField ImageField( name="get_qr_code_url", # Method will be called automatically title="Payment QR", max_width="200px", caption_template="Address: <code>{pay_address}</code>", )

Caption Templates

Use {field_name} placeholders in caption_template:

# Single field caption_template="Scan: {address}" # Multiple fields caption_template="User: {username} | ID: {user_id}" # With HTML caption_template="Pay to: <code>{wallet_address}</code>"

Styling

Control image appearance with CSS properties:

# Fixed size ImageField(width="100px", height="100px") # Maximum constraints ImageField(max_width="300px", max_height="200px") # Circular/rounded ImageField(border_radius="50%") # Circle ImageField(border_radius="8px") # Rounded corners # Combined ImageField( width="80px", height="80px", border_radius="50%", max_width="100px", )

ImagePreviewField

Display images with clickable thumbnails that open a fullscreen modal with zoom and pan capabilities.

Features

  • Click to preview - Thumbnail opens fullscreen modal
  • Mouse wheel zoom - Scroll to zoom in/out
  • Drag to pan - Click and drag to move zoomed image
  • Image info panel - Shows dimensions, filename, format
  • Keyboard support - Escape to close
  • Smart conditions - Show preview only when condition is met
  • Global modal - Single modal instance per page (not duplicated)

Parameters

class ImagePreviewField(FieldConfig): name: str # Field name (ImageField/FileField/URL) title: str | None = None # Display title thumbnail_max_width: int = 200 # Max thumbnail width in pixels thumbnail_max_height: int = 150 # Max thumbnail height in pixels border_radius: int = 8 # Border radius in pixels show_info: bool = True # Show image info in modal zoom_enabled: bool = True # Enable mouse wheel zoom zoom_min: float = 0.5 # Minimum zoom level zoom_max: float = 5.0 # Maximum zoom level zoom_step: float = 0.1 # Zoom step per scroll pan_enabled: bool = True # Enable drag to pan caption: str | None = None # Static caption text caption_field: str | None = None # Model field for caption caption_template: str | None = None # Template with {field} placeholders url_method: str | None = None # Model method that returns URL condition_field: str | None = None # Field to check for showing preview condition_value: Any = True # Expected value for condition fallback_text: str | None = None # Text when condition not met empty_value: str = "—" # Value when None

Basic Usage

from django_cfg.modules.django_admin import ImagePreviewField # Simple preview for ImageField ImagePreviewField( name="photo", title="Photo", thumbnail_max_width=150, ) # Smaller thumbnails ImagePreviewField( name="thumbnail", title="Preview", thumbnail_max_width=100, thumbnail_max_height=100, ) # Circular preview (avatar style) ImagePreviewField( name="avatar", title="Avatar", thumbnail_max_width=80, thumbnail_max_height=80, border_radius=40, # Half of width for circle )

Smart Preview with Conditions

Show preview only for images, fallback for other file types:

# Preview only for images, show badge for other files ImagePreviewField( name="file", title="Media", url_method="get_download_url", # Use method to get URL condition_field="is_image", # Only show preview if is_image=True fallback_text="Not an image", # Badge text for non-images ) # Preview only for specific media types ImagePreviewField( name="media_file", title="Preview", condition_field="media_type", condition_value="photo", # Only for photos fallback_text="No preview", )

Examples

Basic Preview

display_fields=[ ImagePreviewField( name="photo", title="Photo", thumbnail_max_width=150, thumbnail_max_height=150, ), ]

Renders as: Clickable thumbnail → fullscreen modal with zoom/pan

Smart Preview

# TelegramMedia admin - preview only for images display_fields=[ ImagePreviewField( name="file", title="Preview", thumbnail_max_width=100, thumbnail_max_height=100, url_method="get_download_url", condition_field="is_image", fallback_text="Not image", ), ]

Renders as:

  • For images: Clickable thumbnail with preview
  • For videos/docs: Badge “Not image”

With Caption

display_fields=[ ImagePreviewField( name="product_image", title="Product", thumbnail_max_width=200, caption_field="product_name", ), ]

Renders as: Thumbnail with product name below

Auto-Preview for ImageField in Fieldsets

ImageField and FileField in readonly_fields automatically get preview functionality:

config = AdminConfig( model=Product, readonly_fields=["id", "photo", "created_at"], # photo gets auto-preview! fieldsets=[ FieldsetConfig( title="Media", fields=["photo"], # Will show clickable preview with modal ), ], )

Automatic for readonly_fields Any ImageField or FileField in readonly_fields automatically gets the preview modal. No configuration needed!

URL Resolution

ImagePreviewField supports multiple ways to get the image URL:

# 1. Direct ImageField/FileField ImagePreviewField(name="photo") # Uses photo.url # 2. URL CharField ImagePreviewField(name="image_url") # Uses field value directly # 3. Model method ImagePreviewField( name="file", url_method="get_download_url", # Calls obj.get_download_url() ) # 4. Property ImagePreviewField(name="thumbnail_url") # Uses @property value

Real-World Example

from django_cfg.modules.django_admin import ( AdminConfig, ImagePreviewField, ShortUUIDField, BadgeField, DateTimeField, ) telegram_media_config = AdminConfig( model=TelegramMedia, list_display=["id", "file", "media_type", "storage_status", "created_at"], display_fields=[ ShortUUIDField(name="id"), ImagePreviewField( name="file", title="Preview", thumbnail_max_width=100, thumbnail_max_height=100, url_method="get_download_url", condition_field="is_image", fallback_text="Not image", ), BadgeField(name="media_type", label_map={...}), DateTimeField(name="created_at", show_relative=True), ], readonly_fields=["id", "file", "created_at"], # file gets auto-preview in fieldsets! )

The preview modal includes:

  • Zoom controls: +/- buttons and percentage display
  • Reset zoom: Click percentage to reset to 100%
  • Pan: Drag image when zoomed in
  • Image info: Filename, dimensions (WxH), format
  • Keyboard: Escape to close
  • Hint: “Scroll to zoom • Drag to pan • Esc to close”

Styling

# Small thumbnails for list view ImagePreviewField( thumbnail_max_width=80, thumbnail_max_height=80, border_radius=4, ) # Larger preview ImagePreviewField( thumbnail_max_width=200, thumbnail_max_height=150, border_radius=8, ) # Circular (avatar style) ImagePreviewField( thumbnail_max_width=60, thumbnail_max_height=60, border_radius=30, # Half for circle )

vs ImageField

  • ImageField: Simple display, no interactivity
  • ImagePreviewField: Clickable with fullscreen modal, zoom, pan, info panel

Use ImagePreviewField when users need to examine images in detail.

Advanced: ImagePreviewDisplay in computed_field

For custom logic use ImagePreviewDisplay.render():

from django_cfg.modules.django_admin import ImagePreviewDisplay, computed_field @computed_field("Preview") def custom_preview(self, obj): if not obj.is_image: return self.html.badge("No preview", variant="secondary") return ImagePreviewDisplay.render( obj.get_download_url(), config={'thumbnail_max_width': '100px', 'thumbnail_max_height': '100px'} )

VideoField

Display video thumbnails with automatic platform detection for YouTube, Vimeo, and direct video URLs.

Parameters

class VideoField(FieldConfig): name: str # Field name containing video URL title: str | None = None # Display title thumbnail_width: int = 200 # Thumbnail width in pixels thumbnail_height: int = 112 # Thumbnail height (16:9 ratio) border_radius: int = 8 # Border radius in pixels show_inline: bool = False # Show inline player instead of thumbnail show_platform: bool = True # Show platform badge (YT, Vimeo) fallback_text: str | None = None # Text for invalid URLs empty_value: str = "—" # Value when None

Supported Platforms

  • YouTube: youtube.com, youtu.be, youtube.com/shorts
  • Vimeo: vimeo.com
  • Direct: .mp4, .webm, .ogg, .mov files

Basic Usage

from django_cfg.modules.django_admin import VideoField # Simple video thumbnail VideoField(name="video_url") # Custom size VideoField( name="promo_video", thumbnail_width=320, thumbnail_height=180, ) # Inline player (embedded) VideoField( name="tutorial_video", show_inline=True, )

Examples

display_fields=[ VideoField( name="youtube_url", title="Video", thumbnail_width=200, ), ]

Renders as:

  • YouTube thumbnail with play button overlay
  • Platform badge “YT” in corner
  • Click opens video in new tab

Advanced: VideoDisplay in computed_field

from django_cfg.modules.django_admin import VideoDisplay, computed_field @computed_field("Media") def media_preview(self, obj): if obj.media_type == 'video': return VideoDisplay.render(obj.media_url) return self.html.badge("Not video", variant="secondary")

Combining Display Fields

Mix display field types for rich admin displays:

from django_cfg.modules.django_admin import ( AdminConfig, AvatarField, BadgeField, CurrencyField, DateTimeField, Icons, LinkField, UserField, ) config = AdminConfig( model=Order, select_related=["user", "product"], list_display=["order_number", "user", "product", "total", "status", "created_at"], display_fields=[ AvatarField( name="user", # In this case, user is a ForeignKey field, which is valid title="Customer", photo_field="profile_picture", name_field="username", initials_field="first_name", subtitle_field="email", show_as_card=True, ordering="user__username", ), UserField( name="assigned_to", title="Assigned To", header=True, ordering="assigned_to__username", ), LinkField( name="product_name", title="Product", link_field="product_url", link_icon=Icons.OPEN_IN_NEW, subtitle_template="SKU: {product_sku}", ), BadgeField( name="status", title="Status", label_map={ "pending": "warning", "shipped": "primary", "delivered": "success", }, icon=Icons.SHOPPING_CART, ), ], )

Best Practices

1. Use Real Model Fields for name

# ✅ Good - Real model field AvatarField( name="first_name", # CharField in model photo_field="photo", name_field="display_name", # Can be @property initials_field="first_name", ) # ❌ Bad - Virtual field AvatarField( name="user_avatar", # Doesn't exist in model ... )
# ✅ Good - Include ForeignKey relations config = AdminConfig( model=Order, select_related=["user", "assigned_to"], # Important! display_fields=[ UserField(name="user", header=True), UserField(name="assigned_to"), ], )
# ✅ Good - External links open in new tab LinkField( name="website", link_field="url", link_target="_blank", # Default ) # ✅ Good - Internal links stay in same tab LinkField( name="related_order", link_field="order_admin_url", link_target="_self", )

4. Choose the Right Avatar Strategy

# For user listings - use AvatarField AvatarField( name="username", photo_field="photo", name_field="full_name", initials_field="first_name", show_as_card=True, # Rich display ) # For ForeignKey relations - use UserField UserField( name="author", # ForeignKey to User header=True, # Show avatar )

5. Image Caption Best Practices

# ✅ Good - Informative captions ImageField( name="qr_code", caption_template="Scan to pay: <code>{address}</code>", ) # ✅ Good - Field-based captions ImageField( name="photo", caption_field="description", ) # ⚠️ OK - Static captions ImageField( name="thumbnail", caption="Product Image", )

Next Steps