Back to list
sergio-bershadsky

django-dev-unfold

by sergio-bershadsky

3🍴 0📅 Jan 17, 2026

SKILL.md


name: django-dev-unfold description: | This skill should be used when the user asks to "set up unfold admin", "django admin", "customize admin", "admin dashboard", "htmx admin", "unfold theme", or mentions Django admin customization, admin dashboards, or Unfold configuration. Provides Unfold admin patterns with 1-admin-per-file organization and HTMX customization.

Unfold Admin Development

Django Unfold admin patterns with modern UI and HTMX customization.

Core Principles

  1. One admin = one file - Each ModelAdmin in its own file
  2. Unfold base classes - Extend UnfoldAdmin classes
  3. HTMX for dynamics - Use HTMX for interactive features
  4. Dashboard first - Custom dashboards for data visualization
  5. 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
  • django-dev - Core Django patterns
  • django-dev-test - Testing admin functionality

Score

Total Score

60/100

Based on repository quality metrics

SKILL.md

SKILL.mdファイルが含まれている

+20
LICENSE

ライセンスが設定されている

+10
説明文

100文字以上の説明がある

0/10
人気

GitHub Stars 100以上

0/15
最近の活動

3ヶ月以内に更新がある

0/10
フォーク

10回以上フォークされている

0/5
Issue管理

オープンIssueが50未満

+5
言語

プログラミング言語が設定されている

+5
タグ

1つ以上のタグが設定されている

0/5

Reviews

💬

Reviews coming soon