Introduction
Django Unfold provides enhanced form fields that extend Django’s standard fields with additional functionality, particularly for autocomplete and admin integration.
Admin Field Classes
UnfoldAdminField
Custom wrapper for admin form fields with enhanced label rendering:
from unfold.fields import UnfoldAdminField
# Automatically used by Unfold's ModelAdmin
# Handles label rendering with required field indicators
UnfoldAdminField is automatically applied to form fields in the admin. It adds:
Required field asterisks (*) in red
Support for modeltranslation language flags
Proper styling for checkboxes vs regular fields
UnfoldAdminReadonlyField
Enhanced readonly field display with special handling for different field types:
from django.contrib import admin
from unfold.admin import ModelAdmin
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
readonly_fields = [ 'created_at' , 'thumbnail' , 'metadata' ]
# UnfoldAdminReadonlyField automatically:
# - Displays images with previews
# - Shows file download links
# - Formats JSON fields with syntax highlighting
# - Renders URLs as clickable links
# - Handles ManyToMany relationships
Features
Automatically displays image fields with preview and download link. readonly_fields = [ 'profile_picture' ] # Shows image preview
Renders URL fields as clickable links. readonly_fields = [ 'website' ] # Shows as clickable link
Provides download links for file fields. readonly_fields = [ 'document' ] # Shows download link
Preprocessing Readonly Fields
You can preprocess readonly field values before display:
from django.contrib import admin
from unfold.admin import ModelAdmin
def format_phone ( value ):
"""Format phone number for display"""
return f "+1 ( { value[: 3 ] } ) { value[ 3 : 6 ] } - { value[ 6 :] } "
@admin.register (Contact)
class ContactAdmin ( ModelAdmin ):
readonly_fields = [ 'phone_number' ]
readonly_preprocess_fields = {
'phone_number' : format_phone,
}
You can also use dotted import paths:
readonly_preprocess_fields = {
'data' : 'myapp.utils.format_json' ,
}
Autocomplete Fields
UnfoldAdminAutocompleteModelChoiceField
Autocomplete field for ForeignKey relationships:
from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.fields import UnfoldAdminAutocompleteModelChoiceField
from .models import Article, Author
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
autocomplete_fields = [ 'author' ] # Uses UnfoldAdminAutocompleteModelChoiceField
@admin.register (Author)
class AuthorAdmin ( ModelAdmin ):
search_fields = [ 'first_name' , 'last_name' , 'email' ]
When you add a field to autocomplete_fields, Unfold automatically uses the appropriate autocomplete widget.
UnfoldAdminMultipleAutocompleteModelChoiceField
Autocomplete field for ManyToMany relationships:
from django.contrib import admin
from unfold.admin import ModelAdmin
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
autocomplete_fields = [ 'tags' ] # ManyToMany field
@admin.register (Tag)
class TagAdmin ( ModelAdmin ):
search_fields = [ 'name' ]
Custom Autocomplete Implementation
You can manually implement autocomplete fields:
from django import forms
from unfold.fields import (
UnfoldAdminAutocompleteModelChoiceField,
UnfoldAdminMultipleAutocompleteModelChoiceField,
)
from .models import Author, Tag
class ArticleForm ( forms . ModelForm ):
author = UnfoldAdminAutocompleteModelChoiceField(
url_path = 'admin:myapp_author_autocomplete' ,
queryset = Author.objects.all(),
required = True ,
)
tags = UnfoldAdminMultipleAutocompleteModelChoiceField(
url_path = 'admin:myapp_tag_autocomplete' ,
queryset = Tag.objects.all(),
required = False ,
)
class Meta :
model = Article
fields = '__all__'
Field Properties
Field Type Detection
UnfoldAdminReadonlyField automatically detects field types:
JSON Fields
Image Fields
File Fields
URL Fields
@admin.register (Product)
class ProductAdmin ( ModelAdmin ):
readonly_fields = [ 'metadata' ] # JSONField
# Automatically formatted with syntax highlighting
ForeignKey Fields
Automatically creates admin links for related objects:
@admin.register (Book)
class BookAdmin ( ModelAdmin ):
readonly_fields = [ 'author' ] # ForeignKey to Author
# Shows: <a href="/admin/app/author/1/change/">John Doe</a>
ManyToMany Fields
Displays all related objects as comma-separated list:
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
readonly_fields = [ 'tags' ] # ManyToMany to Tag
# Shows: Python, Django, Web Development
OneToOne Fields
Creates admin link to the related object:
@admin.register (User)
class UserAdmin ( ModelAdmin ):
readonly_fields = [ 'profile' ] # OneToOne to Profile
# Shows: <a href="/admin/app/profile/1/change/">User Profile</a>
You can specify custom widgets for autocomplete fields:
from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.widgets import (
UnfoldAdminAutocompleteModelChoiceFieldWidget,
UnfoldAdminMultipleAutocompleteModelChoiceFieldWidget,
)
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
def formfield_for_foreignkey ( self , db_field , request , ** kwargs ):
if db_field.name == 'author' :
kwargs[ 'widget' ] = UnfoldAdminAutocompleteModelChoiceFieldWidget(
attrs = { 'data-ajax--url' : '/admin/autocomplete/authors/' }
)
return super ().formfield_for_foreignkey(db_field, request, ** kwargs)
Boolean Display
Readonly boolean fields show visual indicators:
from django.contrib import admin
from unfold.admin import ModelAdmin
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
readonly_fields = [ 'is_published' ]
# Shows green checkmark for True, red X for False
For custom methods:
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
readonly_fields = [ 'status_badge' ]
@admin.display ( boolean = True , description = 'Published' )
def status_badge ( self , obj ):
return obj.is_published
Empty Value Display
Customize the display for empty/null values:
from django.contrib import admin
from unfold.admin import ModelAdmin
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
readonly_fields = [ 'author' ]
empty_value_display = '-empty-'
# Shows '-empty-' instead of default empty display
Advanced Usage
Change widgets based on conditions:
from django.contrib import admin
from unfold.admin import ModelAdmin
from unfold.widgets import UnfoldAdminTextInputWidget, UnfoldAdminTextareaWidget
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
def formfield_for_dbfield ( self , db_field , request , ** kwargs ):
if db_field.name == 'content' :
if request.user.is_superuser:
kwargs[ 'widget' ] = UnfoldAdminTextareaWidget( attrs = { 'rows' : 20 })
else :
kwargs[ 'widget' ] = UnfoldAdminTextareaWidget( attrs = { 'rows' : 10 })
return super ().formfield_for_dbfield(db_field, request, ** kwargs)
Custom Field Rendering
Override field rendering in templates:
from django.utils.html import format_html
@admin.register (Product)
class ProductAdmin ( ModelAdmin ):
readonly_fields = [ 'formatted_price' ]
def formatted_price ( self , obj ):
return format_html(
'<span style="color: green; font-weight: bold;">$ {} </span>' ,
obj.price
)
formatted_price.short_description = 'Price'
Autocomplete Configuration
Required Setup
For autocomplete fields to work, ensure the related model admin has search_fields:
@admin.register (Author)
class AuthorAdmin ( ModelAdmin ):
search_fields = [
'first_name' ,
'last_name' ,
'email' ,
]
@admin.register (Article)
class ArticleAdmin ( ModelAdmin ):
autocomplete_fields = [ 'author' ] # Requires search_fields in AuthorAdmin
Custom Autocomplete Views
from django.contrib import admin
from unfold.admin import ModelAdmin
from django.http import JsonResponse
@admin.register (Author)
class AuthorAdmin ( ModelAdmin ):
search_fields = [ 'first_name' , 'last_name' ]
def autocomplete_view ( self , request ):
# Custom autocomplete logic
term = request. GET .get( 'term' , '' )
queryset = self .get_search_results(
request,
self .model.objects.all(),
term
)[ 0 ]
results = [
{ 'id' : str (obj.pk), 'text' : str (obj)}
for obj in queryset[: 20 ]
]
return JsonResponse({ 'results' : results})
Best Practices
Use Autocomplete for Large Datasets
Always use autocomplete_fields for ForeignKey/ManyToMany relationships with many objects: autocomplete_fields = [ 'author' , 'tags' , 'category' ]
Ensure related models have proper search_fields defined: search_fields = [ 'name' , 'email' , 'username' ]
Use readonly_preprocess_fields for custom formatting: readonly_preprocess_fields = {
'data' : 'myapp.utils.format_data' ,
}
Use select_related and prefetch_related for readonly fields: def get_queryset ( self , request ):
return super ().get_queryset(request).select_related( 'author' )
Next Steps
Custom Widgets Explore all available form widgets
Conditional Fields Implement conditional field visibility