Skip to main content

Django Admin Fields

Django's admin interface provides a powerful way to manage your application's data. One of its greatest strengths is how easily you can customize the fields displayed in the admin interface without having to build a custom interface from scratch.

Introduction

When working with Django models, you'll often want to control how these models are presented in the admin interface. Django's admin provides several ways to customize the fields that are displayed, how they're displayed, and what actions can be performed on them.

In this tutorial, you'll learn:

  • How to specify which fields appear in the admin
  • How to customize field display and behavior
  • How to add custom fields that aren't directly part of your model
  • Advanced field customizations for better user experience

Basic Field Configuration

Controlling Field Display

The simplest way to control which fields appear in the Django admin is by using the fields and list_display attributes in your ModelAdmin class.

python
from django.contrib import admin
from .models import Book

@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
# Fields to display in the list view
list_display = ('title', 'author', 'publication_date', 'is_bestseller')

# Fields to display in the detail form
fields = ('title', 'author', 'description', 'cover_image', 'publication_date', 'is_bestseller')

In this example:

  • list_display controls which fields appear in the table view of books
  • fields controls which fields appear in the detail/edit form

Grouping Fields with Fieldsets

For forms with many fields, you can organize them using fieldsets:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
fieldsets = (
('Basic Information', {
'fields': ('title', 'author', 'publication_date')
}),
('Additional Details', {
'fields': ('description', 'cover_image'),
'classes': ('collapse',), # Makes this section collapsible
}),
('Status', {
'fields': ('is_bestseller', 'in_stock')
}),
)

The fieldsets attribute takes a tuple of 2-tuples, where each 2-tuple represents a section. The first element is the section title, and the second is a dictionary containing the fields and optional CSS classes.

Customizing Field Display

Read-only Fields

Sometimes you want to display fields that shouldn't be edited:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
readonly_fields = ('created_at', 'updated_at', 'sales_count')

Custom Field Display Methods

You can create custom methods to display derived or formatted data:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'publication_year', 'price_with_currency')

def publication_year(self, obj):
return obj.publication_date.year if obj.publication_date else "N/A"
publication_year.short_description = "Year" # Custom column header

def price_with_currency(self, obj):
return f"${obj.price:.2f}" if obj.price else "Not priced"
price_with_currency.short_description = "Price"

In this example, publication_year extracts just the year from a date field, and price_with_currency formats the price with a dollar sign.

Adding Non-model Fields

You can include fields from related models:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author_name', 'publisher_name')

def author_name(self, obj):
return obj.author.full_name if obj.author else "Unknown"
author_name.short_description = "Author"

def publisher_name(self, obj):
return obj.publisher.name if obj.publisher else "Self-published"
publisher_name.short_description = "Publisher"

Computed Properties

You can add computed properties that don't exist in the database:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'is_recent_publication')

def is_recent_publication(self, obj):
import datetime
if not obj.publication_date:
return False
return obj.publication_date >= datetime.date.today() - datetime.timedelta(days=90)
is_recent_publication.boolean = True # Display as a checkmark/cross icon
is_recent_publication.short_description = "Recent Publication"

The .boolean = True attribute tells Django to display this field as a checkmark or X icon.

Advanced Field Customization

By default, only the first column in list_display is linked to the detail/edit page. You can customize this:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'publication_date')
list_display_links = ('title', 'author') # Both title and author are now clickable

Editable Fields

You can make fields directly editable in the list view:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'is_bestseller', 'in_stock')
list_editable = ('is_bestseller', 'in_stock') # These fields can be edited directly in the list

Note: Fields in list_editable cannot also be in list_display_links.

Custom Ordering

You can specify the default ordering and allow users to sort by custom fields:

python
@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
list_display = ('title', 'author', 'publication_year', 'page_count')
ordering = ('-publication_date',) # Default ordering
sortable_by = ('title', 'author', 'publication_year') # Fields that can be sorted by clicking column headers

def publication_year(self, obj):
return obj.publication_date.year if obj.publication_date else "N/A"

Practical Real-world Examples

E-commerce Product Admin

Here's a more comprehensive example for an e-commerce product model:

python
from django.contrib import admin
from django.utils.html import format_html
from .models import Product

@admin.register(Product)
class ProductAdmin(admin.ModelAdmin):
list_display = ('name', 'thumbnail_preview', 'category', 'price_display', 'stock_status', 'is_active')
list_filter = ('category', 'is_active', 'created_at')
search_fields = ('name', 'description', 'sku')
readonly_fields = ('created_at', 'updated_at', 'image_preview')
list_editable = ('is_active',)

fieldsets = (
('Basic Information', {
'fields': ('name', 'sku', 'description', 'category')
}),
('Pricing', {
'fields': ('price', 'discount_price', 'on_sale')
}),
('Inventory', {
'fields': ('stock_quantity', 'is_active')
}),
('Media', {
'fields': ('image', 'image_preview')
}),
('Metadata', {
'fields': ('created_at', 'updated_at'),
'classes': ('collapse',),
}),
)

def price_display(self, obj):
if obj.on_sale and obj.discount_price:
return format_html(
'<span style="text-decoration: line-through">${:.2f}</span> '
'<span style="color: red">${:.2f}</span>',
obj.price, obj.discount_price
)
return f"${obj.price:.2f}"
price_display.short_description = "Price"

def stock_status(self, obj):
if obj.stock_quantity <= 0:
return format_html('<span style="color: red">Out of Stock</span>')
elif obj.stock_quantity < 10:
return format_html('<span style="color: orange">Low Stock ({0})</span>', obj.stock_quantity)
return format_html('<span style="color: green">In Stock ({0})</span>', obj.stock_quantity)
stock_status.short_description = "Stock Status"

def thumbnail_preview(self, obj):
if obj.image:
return format_html('<img src="{}" width="50" height="50" style="object-fit: cover;" />', obj.image.url)
return "No Image"
thumbnail_preview.short_description = "Thumbnail"

def image_preview(self, obj):
if obj.image:
return format_html('<img src="{}" width="300" />', obj.image.url)
return "No Image"
image_preview.short_description = "Image Preview"

This example demonstrates:

  • Using format_html to safely render HTML in the admin
  • Creating visual representations of data (colored stock status, thumbnail previews)
  • Organizing fields into logical sections
  • Making certain fields read-only for safety
  • Adding different views of the same data (thumbnail in list, larger preview in detail)

Form Field Overrides

You can customize specific form fields using the formfield_overrides attribute:

python
from django.db import models
from django.contrib import admin
from django import forms
from .models import Book

@admin.register(Book)
class BookAdmin(admin.ModelAdmin):
formfield_overrides = {
models.TextField: {'widget': forms.Textarea(attrs={'rows': 20, 'cols': 100})},
models.CharField: {'widget': forms.TextInput(attrs={'size': 50})},
}

This code customizes all TextField and CharField widgets in your form.

Summary

Django Admin's field customization options provide a powerful way to create a tailored administrative interface without writing custom views or templates. We've covered:

  • Controlling which fields are displayed with fields and list_display
  • Organizing fields with fieldsets
  • Creating custom display methods for fields
  • Making fields read-only or directly editable
  • Adding computed properties and fields from related models
  • Advanced customization with HTML formatting and widgets
  • Real-world examples showing these concepts in action

By mastering these techniques, you can create admin interfaces that are more intuitive, efficient, and helpful for content managers and administrators.

Additional Resources

Exercises

  1. Create a Blog model with fields like title, content, author, published_date, and status. Then create a custom admin that:

    • Shows a preview of the first 50 characters of content in the list view
    • Groups fields into "Content", "Publishing", and "Meta" fieldsets
    • Makes the publication date read-only if the status is "published"
  2. For an existing model in your project, add a custom field that displays how long ago an item was created (e.g., "2 days ago", "5 hours ago").

  3. Create a custom admin for a Product model that displays an image thumbnail in the list view and a larger preview in the detail view.

  4. Implement custom filters and search fields to make your admin more user-friendly for a model with many records.



If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)