Skip to content

Admin Mixins

mixins

EnhancedAdminMixin(*args, **kwargs)

Bases: ModelAdmin, EnhancedBaseAdminMixin, ExtraChangelistLinksMixin, ExtraChangeFormButtonsMixin, AjaxActionMixin, CustomFieldsMixin, ActionLinksMixin

Main enhanced admin mixin combining all django-reusable admin features.

Combines EnhancedBaseAdminMixin, ExtraChangelistLinksMixin, ExtraChangeFormButtonsMixin, AjaxActionMixin, CustomFieldsMixin, and ActionLinksMixin into a single drop-in replacement for admin.ModelAdmin.

Attributes:

Name Type Description
auto_update_template

If True, uses enhanced JS-driven templates that inject extra links, buttons, and AJAX actions dynamically. Defaults to True.

default_filters

List of default query-string filter expressions applied when navigating to the changelist without explicit filters, e.g. ['status=active', 'archived=false'].

search_in_choices

List of (field_lookup, label) tuples that populate a "Search In" dropdown filter, allowing users to search within a specific field, e.g. [('first_name__icontains', 'First Name')].

search_in_required

If True, a search query without a "Search In" selection shows a warning and returns no results. Defaults to False.

lazy_load_fields

List of readonly field names whose content should be fetched asynchronously after page load on the change form. The field method should return a placeholder, and a corresponding get_<field_name>_content(self, obj) method provides the real content via an AJAX endpoint. Requires auto_update_template=True.

lazy_list_fields

List of field names whose content should be fetched asynchronously on the changelist. In list_display methods, call self.lazy_list_placeholder(field_name, instance.pk) to render a placeholder. A single batched AJAX call per field fetches content for all visible rows via get_<field_name>_content(self, obj).

Example
class PersonAdmin(EnhancedAdminMixin):
    action_links = [
        ('alert_name', dict(btn_text='Alert', btn_class='btn-info',
                            callback=lambda inst: logger.info(inst))),
    ]
    extra_changelist_links = [
        (reverse_lazy('person_manager'), dict(
            link_text='Manager', link_class='btn btn-warning', new_tab=True)),
    ]
    ajax_action_fields = [
        ('test_ajax', dict(btn_text='Test', callback=lambda a, r, pk: "done")),
    ]
    custom_fields = [
        ('nom', lambda instance: instance.first_name),
    ]
    lazy_load_fields = ['expensive_field']
    lazy_list_fields = ['expensive_column']

    def expensive_field(self, instance):
        return 'Loading...'

    def get_expensive_field_content(self, instance):
        return instance.compute_something_slow()

    def expensive_column(self, instance):
        return self.lazy_list_placeholder('expensive_column', instance.pk)

    def get_expensive_column_content(self, instance):
        return instance.compute_something_slow()

hide_save_buttons(request, object_id)

Hook to conditionally hide save buttons and block POST requests.

Override this to disable editing for specific objects or users.

Parameters:

Name Type Description Default
request

The current HttpRequest.

required
object_id

Primary key of the object being edited.

required

Returns:

Type Description

True to hide save buttons and reject POST, False otherwise.

get_changelist_extra_context(request)

Hook to add extra template context to the changelist view.

Parameters:

Name Type Description Default
request

The current HttpRequest.

required

Returns:

Type Description

Dict of extra context variables merged into the changelist template.

custom_changelist_actions(request)

Hook called at the start of changelist_view for side effects.

Use this to perform actions (e.g. bulk updates) before the changelist is rendered.

Parameters:

Name Type Description Default
request

The current HttpRequest.

required

get_fieldset_section_exclusions(request, obj)

Hook to dynamically exclude entire fieldset sections by title.

Parameters:

Name Type Description Default
request

The current HttpRequest.

required
obj

The model instance being edited, or None for add views.

required

Returns:

Type Description

List of fieldset title strings to exclude.

get_fieldset_field_exclusions(request, obj)

Hook to dynamically exclude individual fields from all fieldsets.

Parameters:

Name Type Description Default
request

The current HttpRequest.

required
obj

The model instance being edited, or None for add views.

required

Returns:

Type Description

List of field name strings to remove from fieldsets.

EnhancedAdminInlineMixin

Bases: InlineModelAdmin

Enhanced inline mixin with select2 support and queryset customization.

Provides auto-updating templates, Select2 widget integration for inline fields, and fine-grained control over field querysets.

Attributes:

Name Type Description
auto_update_template

If True, uses the enhanced admin inline template that supports dynamic JS updates. Defaults to True.

select2_inlines_fields

List of field names to render with Select2 widgets.

default_field_queryset

Dict mapping field names to default querysets, e.g. {'city': City.objects.filter(active=True)}.

limit_field_queryset_model_fields

Dict mapping inline field names to parent model fields used to filter the inline queryset, e.g. {'city': 'country'}.

limit_saved_queryset_value_fields

List of field names whose querysets should be limited to the currently saved value for existing rows.

Example
class MusicianConcertInline(EnhancedAdminInlineMixin, admin.TabularInline):
    model = MusicianConcert
    extra = 0
    select2_inlines_fields = ['city']

EnhancedBaseAdminMixin

Bases: BaseModelAdmin

Base admin mixin that restricts delete permissions to object owners.

When only_delete_owned is True, only the user referenced by user_field on the model instance (or users with the user.is_admin permission) can delete that object.

Attributes:

Name Type Description
user_field

Name of the ForeignKey field on the model that points to the owning user. Defaults to 'created_by'.

only_delete_owned

If True, non-admin users can only delete objects they own. Defaults to False.

AjaxActionMixin

Mixin that adds AJAX-powered action buttons to admin list rows.

Each entry in ajax_action_fields renders as a button in the changelist that triggers a server-side callback via AJAX without a full page reload.

Attributes:

Name Type Description
ajax_action_fields

List of (field_name, config) tuples where config is a dict with keys:

  • btn_text (str): Button label.
  • btn_class (str): CSS classes. Defaults to 'btn btn-warning'.
  • additional_html (str): Extra HTML appended after the button.
  • callback (callable): fn(admin, request, pk) invoked on click.
  • short_desc (str): Column header text.
  • confirm (str): Confirmation prompt shown before the action.
Example
ajax_action_fields = [
    ('test_ajax', dict(
        btn_text='Test Ajax',
        additional_html='<b>hi</b>',
        callback=lambda admin, request, pk: f"done {pk}!",
        confirm='Are you sure?',
    )),
]

ExtraChangelistLinksMixin

Mixin that adds extra link buttons to the admin changelist view.

Attributes:

Name Type Description
extra_changelist_links

List of (url, config) tuples where config is a dict with keys:

  • link_text (str): Link label. Defaults to 'Link'.
  • link_class (str): CSS classes. Defaults to 'btn-link'.
  • new_tab (bool): Open in a new tab. Defaults to False.
  • user_passes_test (callable): fn(user) -> bool to conditionally show the link. Defaults to always visible.
Example
extra_changelist_links = [
    (reverse_lazy('person_manager'), dict(
        link_text='Manager',
        link_class='btn btn-warning',
        new_tab=True,
    )),
]

ExtraChangeFormButtonsMixin

Mixin that adds extra submit buttons to the admin change form.

Buttons appear alongside the default save buttons and trigger custom callbacks on form submission.

Attributes:

Name Type Description
extra_change_form_buttons

List of (name, config) tuples where config is a dict with keys:

  • btn_text (str): Button label. Defaults to 'Button'.
  • btn_class (str): CSS classes. Defaults to 'btn-primary'.
  • stay_on_page (bool): Redirect back to the same change form after the callback. Defaults to False.
  • custom_redirect (bool): If True, the callback's return value is used as the HTTP response. Defaults to False.
  • confirm (str): Confirmation prompt before submission.
  • callback (callable): fn(admin, request, pk) invoked when the button is clicked. Return a message string or an HttpResponse (when custom_redirect=True).
  • user_passes_test (callable): fn(user) -> bool.
  • pk_passes_test (callable): fn(pk) -> bool.
Example
extra_change_form_buttons = [
    ('test', dict(
        btn_text='Test',
        btn_class='btn-warning',
        stay_on_page=True,
        callback=lambda admin, request, pk: print("clicked", pk),
    )),
]

CustomFieldsMixin

Mixin that adds computed read-only fields to the admin.

Each entry becomes a callable on the admin class and is automatically added to readonly_fields.

Attributes:

Name Type Description
custom_fields

List of tuples in the format (field_name, callable, short_desc) where:

  • field_name (str): Name used as the attribute and column key.
  • callable: fn(instance) returning the display value.
  • short_desc (str, optional): Column header text.
Example
custom_fields = [
    ('nom', lambda instance: instance.first_name, 'Nickname'),
]

ActionLinksMixin

Mixin that adds clickable action link buttons per row in the changelist.

Each action link renders as a button that navigates to a custom URL endpoint, executes a callback with the model instance, and redirects back.

Attributes:

Name Type Description
action_links

List of (name, config) tuples where config is a dict with keys:

  • btn_text (str): Button label.
  • btn_class (str): CSS classes for the <a> tag.
  • callback (callable): fn(instance) called when clicked. Required -- entries without callback are skipped.
  • short_desc (str): Column header text.
  • custom_redirect (bool): If True, the callback's return value is used as the HTTP response instead of redirecting back.
Example
action_links = [
    ('alert_name', dict(
        btn_text='Alert Name',
        btn_class='btn-info',
        callback=lambda instance: logger.info(instance),
    )),
    ('go_to_page', dict(
        btn_text='View',
        btn_class='btn-primary',
        custom_redirect=True,
        callback=lambda instance: redirect(instance.get_absolute_url()),
    )),
]

ReadonlyAdmin(model, admin_site)

Bases: ModelAdmin

Read-only admin view that disables add, delete, and editing.

All model fields (except id, created, modified) are displayed as read-only in both the list and change views. Useful for audit/log tables that should be viewable but not modifiable.