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 Type | Purpose | Common Use Cases |
|---|---|---|
| AvatarField | User avatars with initials fallback | Profile pictures, user identity |
| UserField | User display with avatar | Authors, assignees, owners |
| LinkField | Text with external link and subtitle | Usernames, titles, external resources |
| ImageField | Images with captions | QR codes, photos, avatars |
| ImagePreviewField | Image preview with modal zoom/pan | Photos, thumbnails, media galleries |
| VideoField | Video thumbnail with platform detection | YouTube, 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 fieldBasic 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_fieldhas 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 fieldBasic 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
Basic Link
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 • +1234567890Template 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,234Subtitle 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 NoneBasic 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 NoneBasic 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 valueReal-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!
)Modal Features
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 NoneSupported 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
...
)2. Optimize User Fields with select_related
# ✅ 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"),
],
)3. Use Appropriate Link Targets
# ✅ 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
- Badge Fields - BadgeField, StatusBadgesField, CounterBadgeField
- Data Fields - CurrencyField, DateTimeField, TextField, ShortUUIDField
- Configuration Guide - AdminConfig options