Skip to main content

Django Admin Detail View

Django Admin's missing DetailView.

Allows easy creation of a DetailView via DSL for an object in Django Admin.

Companies' changelist added View

Company's DetailView

Contact's DetailView

Why this exists

Django Admin's is missing detail views (by design). It's strengths lie in administering specific objects via form modification.

But, a common need is to allow internal staff/admins to view specific objects and and their related objects. Historically the recommendation was to build a separate website/frontend for this.

Theory

With models Company, ContactInfo, SalesLeads, Orders, Internal staff want to quickly understand the status of a particular Company so a simple View displaying these 4 objects is very beneficial.

Then I can drill down into a Order and see all related Products, OrderStatusUpdates, SalesComments.

Via DSL, it is fast to stand up DetailViews for many objects.

Beliefs

Opinionated, information dense, grid layout. Function over form.

Core API

  • Add View button to changelist for Object.
  • details_table_for() builds object's details table.
  • table_for() builds a list table for related objects.
  • ctx["layout"] holds the grid structure.

Pre-reqs

  • Bootstrap as Webpack
  • webpack_loader

Install

  1. Direct add github to pyproject.toml, git@github.com:jenfi-eng/djadmin-detail-view.git.
  2. Add to INSTALLED_APPS
  3. Create a DetailView and add mixin AdminDetailMixin
  4. To the object's admin add AdminChangeListViewDetail and function get_default_detail_view
  5. Return the newly created DetailView in get_default_detail_view.

See example_project/companies/admin.py for reference.

Code Example

from django.contrib import admin
from django.views.generic import DetailView

from djadmin_detail_view.mixins import AdminChangeListViewDetail, AdminDetailMixin
from djadmin_detail_view.template_helpers import col, detail, details_table_for, table_for

from my_app.companies.models import Company

@admin.register(Company)
class CompanyAdmin(AdminChangeListViewDetail, admin.ModelAdmin):
    def get_default_detail_view(self):
        return CompanyDetailView

class CompanyDetailView(AdminDetailMixin, DetailView):
    model = Company

    def get_context_data(self, request, *args, **kwargs):
        ctx = super().get_context_data(request, *args, **kwargs)

        company_details = details_table_for(
            panel_name="Company Details",
            obj=self.object,
            details=[
                detail("id"),
                detail("legal_name"),
                detail("tax_id"),
                detail("total_completed_order_amount", value=lambda x: x.total_order_value()),
            ]
        )

        # Regular table
        orders_list = table_for(
            panel_name="Orders",
            obj_set=self.object.order_set.all(),
            cols=[
                col("id"),
                col("legal_name"),
                col("total_value"),
                col("created"),
            ]
        )

        # Lazy-loaded table (loads via AJAX after page render)
        large_orders_list = table_for(
            panel_name="Large Orders",
            obj_set=self.object.order_set.filter(total_value__gte=10000),
            cols=[col("id"), col("total_value")],
            lazy_load_key="large_orders",
        )

        ctx["layout"] = [
            {
                "row": [
                    {"col": company_details},
                    {"col": orders_list},
                ],
            },
            {
                "row": [
                    {"col": large_orders_list},
                ],
            },
        ]

        return ctx

Template Helper API Reference

Layout Helpers

  • details_table_for() - Creates a detail table for displaying attributes of a single object
  • detail() - Defines a single detail/column in a details table (alias: col())
  • table_for() - Creates a list table for displaying multiple related objects

Lazy Loading

Both table_for() and details_table_for() support lazy loading to improve initial page load times. When enabled, panels display a spinner and load content via AJAX after the page renders.

# In get_context_data():
orders_list = table_for(
    panel_name="Orders",
    obj_set=self.object.order_set.all(),
    cols=[col("id"), col("status"), col("total")],
    lazy_load_key="orders",  # enables lazy loading with this unique key
    lazy_placeholder="Loading orders...",  # optional
)

Parameters:

  • lazy_load_key - Unique identifier that enables lazy loading for this panel. Must be unique within the page - duplicate keys will raise an error.
  • lazy_placeholder - Custom loading message (default: "Loading...")

How it works:

  1. On initial page load, the panel renders with a Bootstrap spinner placeholder
  2. A Stimulus controller fetches content from the lazy endpoint
  3. The system automatically re-runs get_context_data() with a flag that tells this specific panel to render its actual content
  4. The placeholder is replaced with the loaded content

Error handling:

  • Non-2xx responses display an error message with status code
  • Network errors retry automatically (up to 3 times with exponential backoff)
  • A "Retry" button allows manual retry after failures

Menu Helpers

  • top_menu_btn() - Creates a button for the top menu bar
  • dropdown_item() - Creates an item for the Actions dropdown menu
  • dropdown_divider() - Creates a horizontal divider line in the dropdown menu
  • dropdown_header() - Creates a non-interactive header/label in the dropdown menu

Auto-formatting

The detail() and col() helpers automatically format common data types:

  • DateTime: Formatted using TEMPLATE_TIME_FORMAT setting
  • Date: Formatted using Django's SHORT_DATE_FORMAT
  • Money (moneyed library): Formatted with currency symbol if available
  • ImageFieldFile: Rendered as <img> tag with max dimensions 100x100px
  • None: Displayed as "-"
  • Model instances: Auto-linked to admin detail view if col_name is in AUTOLINK_COL_NAMES (default: ["id", "legal_name"])

Current Open Source Status

It is currently built for Jenfi and its internal needs. Thus, it has specific requirements such as django-hosts and Money that may need to be abstracted away.

PRs are welcome for review to make it more generally accessible.

VSCode

Testing

Add config to allow specs to be run inside VSCode.

// .vscode/settings.json
{
  "python.testing.pytestArgs": [
    ""
  ],
  "python.testing.unittestEnabled": false,
  "python.testing.pytestEnabled": true,
}

Web Launch Config

{
    "name": "Django Web",
    "type": "python",
    "request": "launch",
    "program": "${workspaceFolder}/server.py",
    "args": [],
    "django": true,
    "justMyCode": false
},

TODO

  1. Allow non-webpack based installs.

Jenfi Specific Needs

Credit

This project takes heavy, heavy inspiration from Rail's ActiveAdmin and its DSL.

Metadata

Release files for djadmin-detail-view 0.4.4

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for djadmin-detail-view 0.4.4
File Size Uploaded
djadmin_detail_view-0.4.4.tar.gz 21.3 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for djadmin-detail-view 0.4.4
File Interpreter ABI Platform
djadmin_detail_view-0.4.4-py3-none-any.whl Python 3 none any Details

Total release size: 46.0 kB

Release files / djadmin_detail_view-0.4.4.tar.gz

Download URL djadmin_detail_view-0.4.4.tar.gz
Size 21.3 kB
Tags Source
SHA-256 checksum
How to use checksums
3b625c865cc0546e11d2712925e1d30151c918f693b1fdedf5fdb0a1861e4454
BLAKE2b-256 checksum
How to use checksums
f903606a449277a6eff58bbde6f4835e48ef408dcdcb41c5e87093578a9fae5c
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.2.1 CPython/3.14.2 Darwin/25.1.0

Release files / djadmin_detail_view-0.4.4-py3-none-any.whl

Download URL djadmin_detail_view-0.4.4-py3-none-any.whl
Size 24.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
16e8a721e007b6cc1504acaff8937b2767b56cd82ddfafd6e7f5498e69464cb0
BLAKE2b-256 checksum
How to use checksums
c8e8b654e2308e283dd6e3bb9cab1b1ba462c275bfcc9cde883802734608ff5b
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via poetry/2.2.1 CPython/3.14.2 Darwin/25.1.0

Release history Release notifications | RSS feed

This release

0.4.4 This release

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.4.0

2 release files

0.3.2

2 release files

0.3.1

2 release files

0.3.0

2 release files

0.2.9

2 release files

0.2.8

2 release files

0.2.7

2 release files

0.2.6

2 release files

0.2.5

2 release files

0.2.4

2 release files

0.2.3

2 release files

0.2.2

2 release files

0.2.1

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page