django-dev-unfold

Unfold Admin Development

Safety Notice

This listing is imported from skills.sh public index metadata. Review upstream SKILL.md and repository scripts before running.

Copy this and send it to your AI assistant to learn

Install skill "django-dev-unfold" with this command: npx skills add sergio-bershadsky/ai/sergio-bershadsky-ai-django-dev-unfold

Unfold Admin Development

Django Unfold admin patterns with modern UI and HTMX customization.

Core Principles

  • One admin = one file - Each ModelAdmin in its own file

  • Unfold base classes - Extend UnfoldAdmin classes

  • HTMX for dynamics - Use HTMX for interactive features

  • Dashboard first - Custom dashboards for data visualization

  • Tailwind styling - Consistent Tailwind CSS classes

Installation

pip install django-unfold

Configuration

config/settings.py (with Dynaconf)

INSTALLED_APPS = [ "unfold", "unfold.contrib.filters", "unfold.contrib.forms", "unfold.contrib.inlines", "unfold.contrib.import_export", # Optional "django.contrib.admin", # ... other apps ]

Unfold configuration

UNFOLD = { "SITE_TITLE": "My Admin", "SITE_HEADER": "My Admin", "SITE_URL": "/", "SITE_SYMBOL": "speed", # Material Symbols icon "SHOW_HISTORY": True, "SHOW_VIEW_ON_SITE": True, "ENVIRONMENT": "config.settings.environment_callback", "COLORS": { "primary": { "50": "250 245 255", "100": "243 232 255", "200": "233 213 255", "300": "216 180 254", "400": "192 132 252", "500": "168 85 247", "600": "147 51 234", "700": "126 34 206", "800": "107 33 168", "900": "88 28 135", "950": "59 7 100", }, }, "SIDEBAR": { "show_search": True, "show_all_applications": True, "navigation": [ { "title": "Navigation", "items": [ { "title": "Dashboard", "icon": "dashboard", "link": reverse_lazy("admin:index"), }, { "title": "Users", "icon": "people", "link": reverse_lazy("admin:users_user_changelist"), }, ], }, ], }, }

def environment_callback(request): """Show environment badge in admin.""" from config.settings import settings env = settings.current_env if env == "production": return ["Production", "danger"] elif env == "staging": return ["Staging", "warning"] return ["Development", "info"]

Admin Structure

myapp/ └── admin/ ├── init.py # Register all admins ├── base.py # Base admin classes ├── user.py # UserAdmin ├── product.py # ProductAdmin └── order.py # OrderAdmin

Base Admin Classes

In admin/base.py :

from django.contrib import admin from unfold.admin import ModelAdmin

class BaseModelAdmin(ModelAdmin): """Base admin with common configuration."""

list_per_page = 25
show_full_result_count = False

# Unfold features
compressed_fields = True
warn_unsaved_form = True

def get_queryset(self, request):
    """Exclude soft-deleted by default."""
    qs = super().get_queryset(request)
    if hasattr(self.model, "deleted_at"):
        qs = qs.filter(deleted_at__isnull=True)
    return qs

class ReadOnlyModelAdmin(BaseModelAdmin): """Admin for read-only models."""

def has_add_permission(self, request):
    return False

def has_change_permission(self, request, obj=None):
    return False

def has_delete_permission(self, request, obj=None):
    return False

ModelAdmin Template

Each admin in its own file (admin/user.py ):

from django.contrib import admin from django.utils.html import format_html from unfold.admin import ModelAdmin from unfold.decorators import display

from ..models import User from .base import BaseModelAdmin

@admin.register(User) class UserAdmin(BaseModelAdmin): list_display = ["email", "name", "display_status", "created_at"] list_filter = ["is_active", "created_at"] search_fields = ["email", "name"] ordering = ["-created_at"]

readonly_fields = ["id", "created_at", "updated_at"]

fieldsets = [
    (None, {
        "fields": ["id", "email", "name"],
    }),
    ("Status", {
        "fields": ["is_active"],
        "classes": ["collapse"],
    }),
    ("Timestamps", {
        "fields": ["created_at", "updated_at"],
        "classes": ["collapse"],
    }),
]

@display(
    description="Status",
    label={
        True: "success",
        False: "danger",
    },
)
def display_status(self, obj):
    return obj.is_active

Admin Init

Register all admins in admin/init.py :

from .user import UserAdmin from .product import ProductAdmin from .order import OrderAdmin

all = [ "UserAdmin", "ProductAdmin", "OrderAdmin", ]

Display Decorators

Unfold provides display decorators for styled output:

from unfold.decorators import display

@display( description="Status", label={ "active": "success", "pending": "warning", "cancelled": "danger", }, ) def display_status(self, obj): return obj.status

@display( description="Amount", ordering="total_amount", ) def display_amount(self, obj): return f"${obj.total_amount:,.2f}"

@display( description="Actions", header=True, # Show in header row ) def display_actions(self, obj): return format_html( '<a href="{}" class="btn btn-sm btn-primary">View</a>', obj.get_absolute_url(), )

Filters

Unfold filter types:

from unfold.contrib.filters.admin import ( RangeDateFilter, RangeDateTimeFilter, SingleNumericFilter, RangeNumericFilter, SliderNumericFilter, DropdownFilter, ChoicesDropdownFilter, RelatedDropdownFilter, AutocompleteSelectFilter, )

@admin.register(Order) class OrderAdmin(BaseModelAdmin): list_filter = [ ("created_at", RangeDateFilter), ("total_amount", RangeNumericFilter), ("status", ChoicesDropdownFilter), ("user", RelatedDropdownFilter), ]

Inline Admin

from unfold.admin import TabularInline, StackedInline

class OrderItemInline(TabularInline): model = OrderItem extra = 0 readonly_fields = ["subtotal"]

def subtotal(self, obj):
    return obj.quantity * obj.unit_price

@admin.register(Order) class OrderAdmin(BaseModelAdmin): inlines = [OrderItemInline]

HTMX Actions

See references/customization.md for HTMX patterns including:

  • Dynamic field updates

  • Inline actions

  • Modal dialogs

  • Live search

Dashboard

Custom dashboard in admin/dashboard.py . See references/customization.md .

Additional Resources

Reference Files

  • references/customization.md
  • HTMX patterns, dashboard setup, custom widgets

Related Skills

  • django-dev - Core Django patterns

  • django-dev-test - Testing admin functionality

Source Transparency

This detail page is rendered from real SKILL.md content. Trust labels are metadata-based hints, not a safety guarantee.

Related Skills

Related by shared tags or category signals.

Coding

django-dev-ninja

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

django-dev

No summary provided by upstream source.

Repository SourceNeeds Review
Coding

django-dev-test

No summary provided by upstream source.

Repository SourceNeeds Review
General

frappe-doctype

No summary provided by upstream source.

Repository SourceNeeds Review