A modern, extensible replacement for Django admin
Project description
django-admin-deux
A modern, extensible replacement for Django's admin interface, built on factory patterns and a robust plugin system.
Overview
django-admin-deux (pronounced "django admin two") is a complete reimagining of Django's admin interface. While maintaining familiar concepts and naming conventions, it provides superior extensibility, reusability, and modern UI/UX through a plugin-first architecture.
Key Features
- Plugin Architecture: Built on djp, allowing easy extension and customization
- Factory Pattern: Views are generated dynamically, enabling composition over inheritance
- Feature Advertising: Fail-fast validation ensures plugins provide requested features
- Modern UI: Tailwind-based default theme (coming in Milestone 4)
- Familiar API: If you know Django admin, you'll feel right at home
- Incremental Adoption: Can coexist with Django's built-in admin
Current Status
๐ Milestone 5 Phase 2.7 Complete - Permissions & Authorization System
Major milestones completed:
- โ Milestone 1: Foundation (Plugin system, AdminSite, URL routing, Feature validation)
- โ Milestone 2: Django-Filter Plugin (Filtering, ordering, search via djadmin-filters)
- โ Milestone 3: Layout API & Django-Formset Integration (Forms, inline editing via djadmin-formset)
- โ Milestone 4: Developer Experience (djadmin_inspect, BaseCRUDTestCase, djadmin_apps)
- โ Milestone 5 Phase 2.7: Permissions System (Core permissions, action filtering, ViewAction)
Test Results: 720 passing tests, 82% coverage Plugins Available: djadmin-formset, djadmin-filters Django Support: 5.2, 6.0 Python Support: 3.11, 3.12, 3.13, 3.14
Quick Start
Installation
# Basic installation
pip install django-admin-deux
# With all plugins (djadmin-formset + djadmin-filters)
pip install django-admin-deux[full]
# With specific plugins
pip install django-admin-deux[formset] # Just djadmin-formset
pip install django-admin-deux[filters] # Just djadmin-filters
Basic Usage
1. Add to your INSTALLED_APPS:
# settings.py
from djadmin import djadmin_apps
INSTALLED_APPS = [
# ...
# django-admin-deux (automatically includes all plugins with correct ordering)
*djadmin_apps(),
# ...
]
2. Create a djadmin.py file in your app:
# myapp/djadmin.py
from djadmin import ModelAdmin, register, Layout, Field, Fieldset, Row
from .models import Book
@register(Book)
class BookAdmin(ModelAdmin):
list_display = ['title', 'author', 'published_date']
search_fields = ['title', 'author']
list_filter = ['published_date']
# Optional: Customize form layout
layout = Layout(
Fieldset('Book Information',
Field('title'),
Row(
Field('author', css_classes=['flex-1', 'pr-2']),
Field('published_date', css_classes=['flex-1', 'pl-2']),
),
),
Fieldset('Content',
Field('description', widget='textarea', attrs={'rows': 6}),
),
)
3. Add to your URLs:
# urls.py
from django.urls import path, include
from djadmin import site
urlpatterns = [
path('admin/', admin.site.urls), # Django's admin (optional)
path('djadmin/', include(site.urls)),
path('accounts/', include('django.contrib.auth.urls')), # Required for login/logout
]
4. Visit your admin:
http://localhost:8000/djadmin/
Development Setup
Prerequisites
- Python 3.11 or higher
- just command runner (optional but recommended)
- Git
Clone and Setup
# Clone the repository
git clone https://codeberg.org/emmaDelescolle/django-admin-deux.git
cd django-admin-deux
# Run the setup script
./setup_dev.sh
# Or manually:
python -m venv .venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
pip install -e ".[dev]"
pre-commit install
Development Commands
With just installed:
# Run tests
just test
# Run tests with coverage
just test-coverage
# Format code
just format
# Run linters
just lint
# Run development server
just runserver
# See all commands
just --list
Without just:
# Run tests
pytest
# Run tests with coverage
pytest --cov=djadmin --cov-report=html
# Format code
ruff format .
ruff check . --fix
djlint djadmin/ --reformat
# Run linters
ruff check .
djlint djadmin/ --lint
# Run development server
cd tests && python manage.py runserver
Project Structure
django-admin-deux/
โโโ djadmin/ # Main package
โ โโโ plugins/ # Plugin system
โ โโโ sites.py # AdminSite class
โ โโโ options.py # ModelAdmin class
โ โโโ decorators.py # @register decorator
โโโ examples/
โ โโโ webshop/ # Example e-commerce app
โโโ tests/ # Test infrastructure
โโโ pyproject.toml # Package configuration
โโโ tox.ini # CI test matrix
Key Features
Architecture
Plugin System
django-admin-deux uses djp for its plugin architecture. Plugins can:
- Add mixins to views
- Provide default actions
- Modify querysets
- Add context data
- Provide CSS/JS assets
Example plugin:
# myapp/djadmin_hooks.py
from djadmin.plugins import hookimpl
@hookimpl
def djadmin_provides_features():
"""Advertise features this plugin provides"""
return ['search', 'filter']
@hookimpl
def djadmin_get_action_view_mixins(action):
"""Add search functionality to ListView"""
from djadmin.plugins.core.actions import ListAction
from .mixins import SearchMixin
return {
ListAction: [SearchMixin]
}
Feature Advertising
ModelAdmin configurations are validated at startup. If you request a feature (like search or filtering) but no plugin provides it, you'll get a clear error:
class BookAdmin(ModelAdmin):
search_fields = ['title'] # Requires 'search' feature
# If no plugin provides 'search':
# ImproperlyConfigured: ModelAdmin BookAdmin requests features {'search'}
# but no registered plugin provides them.
View Factory Pattern
Views are generated dynamically using class-based factories, allowing plugins to inject mixins and customize behavior without complex inheritance chains.
# Conceptual example (Milestone 2)
class ListViewFactory:
def create_view(self, model, admin_class, plugins):
# Collect mixins from plugins
mixins = []
for plugin in plugins:
mixins.extend(plugin.get_list_view_mixins())
# Generate view class dynamically
view_class = type(
f'{model.__name__}ListView',
tuple(mixins + [BaseListView]),
{'model': model, 'admin': admin_class}
)
return view_class
Layout API
django-admin-deux provides a powerful Layout API for customizing form layouts with progressive enhancement:
from djadmin import ModelAdmin, register, Layout, Field, Fieldset, Row
@register(Author)
class AuthorAdmin(ModelAdmin):
layout = Layout(
Fieldset('Personal Information',
Row(
Field('first_name', css_classes=['flex-1', 'pr-2']),
Field('last_name', css_classes=['flex-1', 'pl-2']),
),
Field('birth_date', label='Date of Birth'),
),
Fieldset('Biography',
Field('bio', widget='textarea', attrs={'rows': 8}),
),
)
Action-Specific Layouts:
Use different layouts for create vs update actions (follows the same pattern as create_form_class/update_form_class):
@register(Product)
class ProductAdmin(ModelAdmin):
# Create-specific layout (simpler, focused on essentials)
create_layout = Layout(
Fieldset('New Product',
Field('name', required=True),
Row(
Field('price', css_classes=['flex-1']),
Field('cost', css_classes=['flex-1']),
),
),
)
# Update-specific layout (includes metadata fields)
update_layout = Layout(
Fieldset('Product Information',
Field('name'),
Field('description', widget='textarea'),
),
Fieldset('Metadata',
Field('created_at', widget=DateTimeInput(attrs={'readonly': True})),
Field('updated_at', widget=DateTimeInput(attrs={'readonly': True})),
),
)
Core Features (no plugin required):
- โ Fieldsets - Group fields with legends and descriptions
- โ Rows - Horizontal layouts using flexbox
- โ Field Customization - Labels, widgets, help text, CSS classes
- โ
Widget Shortcuts - Use strings like
'textarea','email' - โ
Django Admin Migration - Automatic conversion of
fieldsets - โ
Action-Specific Layouts - Different layouts for create/update (
create_layout,update_layout)
Plugin Features (with djadmin-formset):
- โ Collections - Inline editing of related objects
- โ Conditional Fields - Show/hide fields based on values
- โ Computed Fields - Auto-calculate values
- โ Client-side Validation - Instant feedback
- โ Drag-and-drop - Reorder inline items
Learn more: Layout API Documentation
Testing
The project uses pytest with extensive test coverage:
# Run all tests
just test
# Run specific test
just test-file tests/test_plugins.py
# Run tests matching pattern
just test-match test_register
# Run with coverage report
just test-coverage
Test Organization
tests/- Infrastructure tests (plugins, URLs, validation)examples/webshop/tests/- Integration tests using example models
Test Best Practices: Avoiding Test Pollution
When writing tests that register/unregister ModelAdmin classes, follow the DynamicURLConf pattern to avoid test pollution (tests that pass individually but fail in the suite):
from django.test import TestCase, override_settings
from django.urls import clear_url_caches
from djadmin import ModelAdmin, site
# Dynamic URLconf regenerates on each access
class DynamicURLConf:
@property
def urlpatterns(self):
from django.urls import path, include
return [path('djadmin/', include(site.urls))]
@override_settings(ROOT_URLCONF=DynamicURLConf())
class TestMyFeature(TestCase):
def setUp(self):
# Clean registry before test
if MyModel in site._registry:
site.unregister(MyModel)
self.my_objects = MyModelFactory.create_batch(5)
def tearDown(self):
# Clean registry and caches after test
if MyModel in site._registry:
site.unregister(MyModel)
clear_url_caches()
if hasattr(self.client, '_cached_urlconf'):
delattr(self.client, '_cached_urlconf')
def test_my_feature(self):
# Register with override=True
class MyModelAdmin(ModelAdmin):
list_display = ['field1', 'field2']
site.register(MyModel, MyModelAdmin, override=True)
# ... test code
Why this is needed: Django caches URL patterns and view closures, so without this pattern, tests will use stale admin configurations from previous tests.
Reference: This pattern is based on https://แฎ.cc/2019/11/09/django-testing-dynamic-urlconf.html
See tests/test_search.py for a complete example.
Example Models
The webshop example app provides realistic test data:
Category- Hierarchical product categoriesProduct- Products with SKU, pricing, stockCustomer- Customer accounts with addressesOrder- Orders with status trackingOrderItem- Line items in ordersReview- Product reviews with ratingsTag- Tags (many-to-many relationship example)
All models have corresponding factory_boy factories for easy test data creation.
Continuous Integration
The project uses GitLab CI with tox to test all Python/Django combinations:
- Python: 3.11, 3.12, 3.13, 3.14
- Django: 5.2, 6.0
Pre-commit hooks run on every commit to catch issues early:
- Ruff (linting and formatting)
- djLint (Django template linting)
- pytest (test suite)
Roadmap
Milestone 1: Foundation โ Complete
- โ Plugin system with djp
- โ AdminSite and ModelAdmin classes
- โ URL routing
- โ Feature validation
- โ Basic templates
Milestone 2: View Factories & Actions โ Complete
- โ Factory pattern implementation
- โ ListView, CreateView, DetailView
- โ Form handling
- โ List actions (e.g., "Add New")
- โ Bulk actions (e.g., "Delete Selected")
- โ Record actions (e.g., "Edit", "Delete")
Milestone 3: Layout API & Django-Formset Integration โ Complete
- โ Core Layout API (Field, Fieldset, Row, Collection)
- โ Automatic Django admin fieldsets conversion
- โ Feature advertising system
- โ Basic flexbox rendering
- โ FormFactory for django-formset integration
- โ Inline editing (Collections)
- โ Conditional fields (show_if/hide_if)
- โ Computed fields (calculate)
- โ 565 tests passing, 87% coverage
Milestone 4: Developer Experience โ Complete
- โ djadmin_inspect management command
- โ BaseCRUDTestCase for automated testing
- โ Plugin-driven INSTALLED_APPS
- โ Comprehensive documentation
Milestone 5: Permissions System (Current - Phase 2.7 Complete)
- โ Core permission classes (IsAuthenticated, IsStaff, HasDjangoPermission)
- โ Composition operators (AND/OR/NOT)
- โ ModelAdmin permission integration
- โ Action-level permissions
- โ Object-level permissions support
- โ Action filtering based on permissions
- โ ViewAction for view-only users
- โ Dashboard filtering
- โ 720 tests passing, 82% coverage
- ๐ Next: Guardian plugin (optional), UI integration & polish
Milestone 6: Quality & Polish (Planned)
- Coverage improvements (>90%)
- Accessibility audit (WCAG 2.1 AA)
- Performance benchmarking
- CI/CD infrastructure
- Production-ready release
Contributing
We welcome contributions! The project is in early development, so there are many opportunities to help shape the future of Django admin interfaces.
Development Workflow
- Fork and clone the repository
- Create a feature branch:
git checkout -b feature/my-feature - Make your changes and add tests
- Run tests and linters:
just test && just lint - Commit (pre-commit hooks will run automatically)
- Push and create a merge request
Code Style
- Python: Ruff formatter (120 character line length)
- Templates: djLint formatter
- Commits: Use conventional commit format (e.g.,
feat:,fix:,docs:) - Tests: Pytest with >80% coverage required
Running Tests Locally
# Run full test suite
just test
# Test specific Python/Django combination
tox -e py311-django52
# Test all combinations (like CI)
tox
Documentation
User Documentation
- Layout API Overview - Introduction to the Layout API
- Component Reference - Detailed API for each component
- Django Admin Migration Guide - Migrate from Django admin
- Layout Examples - Real-world usage patterns
Developer Documentation
- CLAUDE.md - Technical documentation for AI assistants
- PRD - Complete product requirements (v2.7)
- Milestone Plans - Implementation plans archive
- CHANGELOG - Version history and release notes
License
This project is licensed under the MIT License - see the LICENSE file for details.
Acknowledgments
- Inspired by django-admin2
- Built with djp by Simon Willison
- Uses factory_boy for test data
- Styled with Tailwind CSS (coming in Milestone 4)
Status: ๐ Milestone 5 Phase 2.7 Complete Python: 3.11+ Django: 5.2+ License: MIT
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file django_admin_deux-0.1.6.tar.gz.
File metadata
- Download URL: django_admin_deux-0.1.6.tar.gz
- Upload date:
- Size: 124.3 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
5ccd36a46c540f488df8ce6515b6d147fe8f1966b9179bec53fa09f5f47a4e1a
|
|
| MD5 |
28d857a8f3a5b267978934204a9031f2
|
|
| BLAKE2b-256 |
151e25b3f15618ce2c98029a7fbaa1cf5dd93258f050a0d15413401df57b4188
|
File details
Details for the file django_admin_deux-0.1.6-py3-none-any.whl.
File metadata
- Download URL: django_admin_deux-0.1.6-py3-none-any.whl
- Upload date:
- Size: 148.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4ea2e1b6764779faaa7d8b6d66eb22ee2c24cd0a1a33a963b49c9ba5f7313f3c
|
|
| MD5 |
1c34b71624f3ad2d10aa20ad8173ce1a
|
|
| BLAKE2b-256 |
72c771b0c5abda4a4a055fdceb1a69ed3739d683ab10fceee12efb43fd8d45ad
|