Skip to content

ModelAdmin API Reference

The ModelAdmin class encapsulates all admin options and functionality for a model. It serves as the central configuration point for how a model is displayed and interacted with in the admin interface.

Class Definition

from djadmin import ModelAdmin

class ModelAdmin(metaclass=ModelAdminMetaclass):
    """
    Encapsulates all admin options and functionality for a model.
    """

Constructor

def __init__(self, model, admin_site: AdminSite)

Parameters

  • model (type): The Django model class this admin is managing
  • admin_site (AdminSite): The AdminSite instance this admin is registered to

Notes

  • ModelAdmin instances are typically created automatically by AdminSite.register() or the @register decorator
  • You rarely need to instantiate ModelAdmin directly

ListView Configuration

list_display

list_display: list[str | Callable | Column] | None = None

Specifies which fields/columns to display in the list view.

Type: List of strings, callables, or Column instances Default: None (will be initialized to [Column('__str__')])

Examples:

# Simple field names (Django admin style)
class ProductAdmin(ModelAdmin):
    list_display = ['name', 'sku', 'price']

# With callables
class ProductAdmin(ModelAdmin):
    def formatted_price(self, obj):
        return f"${obj.price:.2f}"

    list_display = ['name', 'sku', formatted_price]

# Using Column for enhanced configuration
from djadmin import Column

class ProductAdmin(ModelAdmin):
    list_display = [
        Column('name'),
        Column('sku', label='SKU Code', classes='font-mono'),
        Column('price', empty_value='N/A'),
    ]

# Mixed style
class ProductAdmin(ModelAdmin):
    list_display = [
        'name',
        Column('sku', label='SKU Code'),
        'price',
    ]

See also: Column API Reference

general_actions

general_actions: list[type[BaseAction]] | None = None

Actions available from the list view that don't require record selection.

Type: List of Action classes Default: None (uses plugin-provided defaults: [ListAction, AddAction])

Examples:

from djadmin import ModelAdmin
from djadmin.actions import BaseAction, GeneralActionMixin
from djadmin.actions.view_mixins import TemplateViewActionMixin

class ImportAction(GeneralActionMixin, TemplateViewActionMixin, BaseAction):
    label = 'Import'
    icon = 'upload'

    def get_template_name(self):
        return 'myapp/import.html'

class ProductAdmin(ModelAdmin):
    # Override default general_actions
    general_actions = [ListAction, AddAction, ImportAction]

Default Actions (from core plugin): - ListAction - Main list view - AddAction - Create new record

See also: Actions API Reference

bulk_actions

bulk_actions: list[type[BaseAction]] | None = None

Actions that operate on multiple selected records from checkboxes.

Type: List of Action classes Default: None (uses plugin-provided defaults: [DeleteBulkAction])

Examples:

from djadmin import ModelAdmin
from djadmin.actions import BaseAction, BulkActionMixin
from djadmin.actions.view_mixins import FormViewActionMixin
from django import forms

class BulkStatusForm(forms.Form):
    status = forms.ChoiceField(choices=[('active', 'Active'), ('inactive', 'Inactive')])

class BulkChangeStatusAction(BulkActionMixin, FormViewActionMixin, BaseAction):
    label = 'Change Status'
    form_class = BulkStatusForm

    def get_template_name(self):
        return 'myapp/bulk_status.html'

class ProductAdmin(ModelAdmin):
    bulk_actions = [DeleteBulkAction, BulkChangeStatusAction]

Default Actions (from core plugin): - DeleteBulkAction - Delete selected records

See also: Actions API Reference

record_actions

record_actions: list[type[BaseAction]] | None = None

Actions that operate on a single record (displayed in list rows or detail pages).

Type: List of Action classes Default: None (uses plugin-provided defaults: [EditAction, DeleteAction])

Examples:

from djadmin import ModelAdmin
from djadmin.actions import BaseAction, RecordActionMixin
from djadmin.actions.view_mixins import TemplateViewActionMixin

class DuplicateAction(RecordActionMixin, TemplateViewActionMixin, BaseAction):
    label = 'Duplicate'
    icon = 'copy'

    def get_template_name(self):
        return 'myapp/duplicate.html'

class ProductAdmin(ModelAdmin):
    record_actions = [EditAction, DeleteAction, DuplicateAction]

Default Actions (from core plugin): - EditAction - Edit record - DeleteAction - Delete record

See also: Actions API Reference

paginate_by

paginate_by: int = 100

Number of records to display per page in the list view.

Type: int Default: 100

Example:

class ProductAdmin(ModelAdmin):
    paginate_by = 50  # Show 50 products per page

pagination_class

pagination_class: type | None = None

Custom pagination class for the list view.

Type: Django pagination class or None Default: None (uses Django's default Paginator)

Example:

from django.core.paginator import Paginator

class CustomPaginator(Paginator):
    def __init__(self, *args, **kwargs):
        kwargs['orphans'] = 5  # Move last few items to previous page
        super().__init__(*args, **kwargs)

class ProductAdmin(ModelAdmin):
    pagination_class = CustomPaginator

Form Configuration

form_class

form_class: type[ModelForm] | None = None

Base form class used for both create and update views (unless overridden by create_form_class or update_form_class).

Type: ModelForm class or None Default: None (auto-generates form)

Example:

from django import forms
from myapp.models import Product

class ProductForm(forms.ModelForm):
    class Meta:
        model = Product
        fields = '__all__'
        widgets = {
            'description': forms.Textarea(attrs={'rows': 5}),
        }

class ProductAdmin(ModelAdmin):
    form_class = ProductForm  # Used for both create and update

fields

fields: list[str] | Literal['__all__'] = '__all__'

Base field list for both create and update forms (unless overridden by create_fields or update_fields).

Type: List of field names or '__all__' Default: '__all__'

Example:

class ProductAdmin(ModelAdmin):
    fields = ['name', 'sku', 'price', 'description']  # Exclude 'created_at'

CreateView Configuration

create_form_class

create_form_class: type[ModelForm] | None = None

Form class specifically for creating new records. Overrides form_class for create views.

Type: ModelForm class or None Default: None (falls back to form_class)

Example:

class ProductCreateForm(forms.ModelForm):
    # Special fields only needed during creation
    send_notification = forms.BooleanField(required=False)

    class Meta:
        model = Product
        fields = '__all__'

class ProductAdmin(ModelAdmin):
    create_form_class = ProductCreateForm
    form_class = ProductForm  # Used for updates

create_fields

create_fields: list[str] | Literal['__all__'] | None = None

Field list specifically for creating new records. Overrides fields for create views.

Type: List of field names, '__all__', or None Default: None (falls back to fields)

Example:

class ProductAdmin(ModelAdmin):
    create_fields = ['name', 'sku', 'price']  # Limited fields for creation
    update_fields = ['name', 'price', 'description', 'status']  # More fields for updates

create_view_class

create_view_class: type | None = None

Custom view class for create operations. Bypasses ViewFactory if provided.

Type: Django view class or None Default: None (uses ViewFactory)

Example:

from django.views.generic import CreateView

class CustomProductCreateView(CreateView):
    template_name = 'custom_create.html'

    def form_valid(self, form):
        # Custom creation logic
        response = super().form_valid(form)
        send_notification(form.instance)
        return response

class ProductAdmin(ModelAdmin):
    create_view_class = CustomProductCreateView

DetailView/UpdateView Configuration

update_form_class

update_form_class: type[ModelForm] | None = None

Form class specifically for updating existing records. Overrides form_class for detail/update views.

Type: ModelForm class or None Default: None (falls back to form_class)

Example:

class ProductUpdateForm(forms.ModelForm):
    # Additional fields for updates
    change_reason = forms.CharField(required=False)

    class Meta:
        model = Product
        fields = '__all__'

class ProductAdmin(ModelAdmin):
    update_form_class = ProductUpdateForm

update_fields

update_fields: list[str] | Literal['__all__'] | None = None

Field list specifically for updating existing records. Overrides fields for detail/update views.

Type: List of field names, '__all__', or None Default: None (falls back to fields)

Example:

class ProductAdmin(ModelAdmin):
    update_fields = ['name', 'price', 'description', 'status']  # Can't change SKU
    create_fields = ['name', 'sku', 'price']  # SKU required at creation

detail_view_class

detail_view_class: type | None = None

Custom view class for detail/update operations. Bypasses ViewFactory if provided.

Type: Django view class or None Default: None (uses ViewFactory)

Example:

from django.views.generic import UpdateView

class CustomProductUpdateView(UpdateView):
    template_name = 'custom_update.html'

    def get_queryset(self):
        # Custom queryset logic
        return super().get_queryset().select_related('category')

class ProductAdmin(ModelAdmin):
    detail_view_class = CustomProductUpdateView

ListView Override

list_view_class

list_view_class: type | None = None

Custom view class for list operations. Bypasses ViewFactory if provided.

Type: Django view class or None Default: None (uses ViewFactory)

Example:

from django.views.generic import ListView

class CustomProductListView(ListView):
    template_name = 'custom_list.html'

    def get_queryset(self):
        # Custom filtering
        qs = super().get_queryset()
        if not self.request.user.is_superuser:
            qs = qs.filter(status='active')
        return qs

class ProductAdmin(ModelAdmin):
    list_view_class = CustomProductListView

Feature Indicators

Feature indicators are attributes that trigger plugin requirements. When set, they indicate that your ModelAdmin needs specific plugin features.

search_fields

search_fields: list[str] | None = None

Enables search functionality (requires 'search' feature from a plugin).

Type: List of field names or None Default: None Triggers: 'search' feature requirement

Example:

class ProductAdmin(ModelAdmin):
    search_fields = ['name', 'sku', 'description']
    # This requires a plugin that provides the 'search' feature

list_filter

list_filter: list[str | tuple] | None = None

Enables filtering sidebar (requires 'filter' feature from a plugin).

Type: List of field names or filter specifications, or None Default: None Triggers: 'filter' feature requirement

Example:

class ProductAdmin(ModelAdmin):
    list_filter = ['status', 'category', 'created_at']
    # This requires a plugin that provides the 'filter' feature

ordering

ordering: list[str] | None = None

Default ordering for list view (requires 'ordering' feature from a plugin).

Type: List of field names (prefix with '-' for descending), or None Default: None Triggers: 'ordering' feature requirement

Example:

class ProductAdmin(ModelAdmin):
    ordering = ['-created_at', 'name']  # Newest first, then by name

inlines

inlines: list[type] | None = None

Inline model admin classes for related models (requires 'inlines' feature from a plugin).

Type: List of InlineModelAdmin classes or None Default: None Triggers: 'inlines' feature requirement

Example:

class OrderItemInline(InlineModelAdmin):
    model = OrderItem
    extra = 1

class OrderAdmin(ModelAdmin):
    inlines = [OrderItemInline]
    # This requires a plugin that provides the 'inlines' feature

inline_sortable

inline_sortable: bool = False

Enable drag-and-drop sorting for inlines (requires 'sortable' feature from a plugin).

Type: bool Default: False Triggers: 'sortable' feature requirement

Example:

class ProductAdmin(ModelAdmin):
    inlines = [ImageInline]
    inline_sortable = True  # Enable drag-and-drop for images

Feature Advertising

FEATURE_INDICATORS

FEATURE_INDICATORS: ClassVar[dict[str, list[str]]] = {
    'search': ['search_fields'],
    'filter': ['list_filter'],
    'ordering': ['ordering'],
    'inlines': ['inlines'],
    'sortable': ['inline_sortable'],
}

Class-level mapping of features to their indicator attributes.

Type: dict[str, list[str]] Use: Internal - defines which attributes trigger which feature requirements

Custom Features:

You can extend FEATURE_INDICATORS to add custom feature detection:

class ProductAdmin(ModelAdmin):
    FEATURE_INDICATORS = {
        **ModelAdmin.FEATURE_INDICATORS,
        'export': ['export_formats'],  # Custom feature
    }

    export_formats = ['csv', 'xlsx']  # Triggers 'export' feature

requested_features

@property
def requested_features(self) -> set:
    """Get set of features requested by checking indicator attributes."""

Returns the set of features that this ModelAdmin requests based on which feature indicator attributes are set.

Returns: set[str] - Set of feature names

Example:

class ProductAdmin(ModelAdmin):
    search_fields = ['name']
    list_filter = ['category']

admin = ProductAdmin(Product, site)
print(admin.requested_features)  # {'search', 'filter'}

Notes: - Called automatically at Django startup for validation - Raises ImproperlyConfigured if a requested feature isn't provided by any plugin - Uses truthy check on attributes (empty lists don't trigger features)

Properties

opts

@property
def opts(self):
    """Shortcut to model._meta"""

Convenient access to the model's _meta object.

Returns: django.db.models.options.Options

Example:

admin = ProductAdmin(Product, site)
print(admin.opts.verbose_name)  # 'product'
print(admin.opts.app_label)     # 'webshop'

list_url_name

@property
def list_url_name(self):
    """Get URL name for list view (without namespace)"""

Returns the URL name for the list view of this model.

Returns: str - URL name (e.g., 'webshop_product_list')

Example:

admin = ProductAdmin(Product, site)
print(admin.list_url_name)  # 'webshop_product_list'

# Use with reverse:
from django.urls import reverse
url = reverse(f'djadmin:{admin.list_url_name}')

Form/Fields Resolution Logic

The ModelAdmin resolves which form and fields to use based on a fallback hierarchy:

For CreateView (AddAction):

create_form_class = self.create_form_class or self.form_class or auto_generated_form
create_fields = self.create_fields or self.fields or '__all__'

For DetailView/UpdateView (EditAction):

update_form_class = self.update_form_class or self.form_class or auto_generated_form
update_fields = self.update_fields or self.fields or '__all__'

Example:

class ProductAdmin(ModelAdmin):
    # Base configuration (used if specific not set)
    form_class = ProductForm
    fields = '__all__'

    # Create-specific (overrides base)
    create_fields = ['name', 'sku', 'price']

    # Update-specific (overrides base)
    update_form_class = ProductUpdateForm
    update_fields = ['name', 'price', 'description']

# Results:
# - Create: uses ProductForm with fields ['name', 'sku', 'price']
# - Update: uses ProductUpdateForm with fields ['name', 'price', 'description']

Usage Examples

Basic Registration

from djadmin import ModelAdmin, register
from myapp.models import Product

@register(Product)
class ProductAdmin(ModelAdmin):
    list_display = ['name', 'sku', 'price']
    paginate_by = 50

Advanced Configuration

from djadmin import ModelAdmin, register, Column
from djadmin.actions import BaseAction, GeneralActionMixin
from myapp.models import Product

class ExportAction(GeneralActionMixin, BaseAction):
    label = 'Export'
    # ... implementation

@register(Product)
class ProductAdmin(ModelAdmin):
    # List view
    list_display = [
        Column('name'),
        Column('sku', label='SKU Code', classes='font-mono'),
        Column('price', empty_value='N/A'),
        'status',
    ]
    paginate_by = 25

    # Actions
    general_actions = [ListAction, AddAction, ExportAction]
    bulk_actions = [DeleteBulkAction]
    record_actions = [EditAction, DeleteAction]

    # Forms
    form_class = ProductForm
    create_fields = ['name', 'sku', 'price', 'category']
    update_fields = ['name', 'price', 'description', 'status', 'category']

Custom View Classes

from django.views.generic import ListView, CreateView

class CustomProductListView(ListView):
    paginate_by = 10

    def get_queryset(self):
        qs = super().get_queryset()
        return qs.filter(status='active').select_related('category')

class CustomProductCreateView(CreateView):
    def form_valid(self, form):
        response = super().form_valid(form)
        notify_team(form.instance)
        return response

@register(Product)
class ProductAdmin(ModelAdmin):
    list_view_class = CustomProductListView
    create_view_class = CustomProductCreateView

See Also