Developer Experience¶
Milestone 4 delivers essential tools for building and testing with django-admin-deux. These tools improve development velocity, debugging, and code quality throughout the development lifecycle.
Overview¶
Developer Experience (DX) features make working with django-admin-deux faster and more enjoyable:
- Introspection: Understand your admin configuration at a glance
- Testing: Comprehensive CRUD testing with minimal boilerplate
- Configuration: Zero-config plugin setup with automatic dependency resolution
- Debugging: Clear visibility into how your admin is composed
Tools & Features¶
1. Admin Introspection (djadmin_inspect)¶
Management command for understanding your admin configuration
# Inspect all registered admins
python manage.py djadmin_inspect
# Inspect specific model
python manage.py djadmin_inspect --model myapp.MyModel
# JSON output for automation
python manage.py djadmin_inspect --format json
What it shows: - All registered actions (general, bulk, record) - View composition (base class + mixins) - Feature availability (which plugins provide what) - Form configuration - Template resolution order
When to use: - Debugging why a feature isn't working - Understanding plugin contributions - Finding URL names - Verifying form configuration
2. CRUD Testing (BaseCRUDTestCase)¶
Base test class for automatic CRUD operation testing
from djadmin.testing import BaseCRUDTestCase
from myapp.factories import MyModelFactory
from myapp.models import MyModel
class TestMyModelAdmin(BaseCRUDTestCase):
model = MyModel
model_factory_class = MyModelFactory
# That's it! Automatically tests all CRUD operations
What it tests: - List view (GET) - Create view (GET + POST) - Update view (GET + POST) - Delete view (POST)
Key features: - Action introspection (works with any admin configuration) - Plugin-based test methods (extensible by plugins) - Factory integration (FactoryBoy) - 80%+ boilerplate reduction
3. Plugin-Driven Apps (djadmin_apps())¶
Zero-configuration INSTALLED_APPS setup
# settings.py
from djadmin import djadmin_apps
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
# ... your apps
] + djadmin_apps() # Automatically adds djadmin + all plugins
What it does:
- Discovers third-party plugins via Python entrypoints
- Registers built-in plugins (core, theme)
- Resolves ordering using First, Before, After, Position modifiers
- Removes duplicates automatically
Benefits: - Single function call in settings - Plugins declare dependencies in code (not docs) - Automatic ordering resolution - No configuration required
Quick Start¶
Installation¶
# Install django-admin-deux with dev dependencies
pip install -e ".[dev]"
# Or with uv
uv pip install -e ".[dev]"
Setup¶
# Run setup script (installs dependencies + pre-commit hooks)
./setup_dev.sh
# Or manually:
python -m venv venv
source venv/bin/activate
pip install -e ".[dev]"
pre-commit install
First Steps¶
-
Inspect your admin configuration:
-
Set up INSTALLED_APPS:
-
Write tests with BaseCRUDTestCase:
Common Workflows¶
Debugging Feature Issues¶
When a feature (like search or filters) isn't working:
# 1. Check what features are requested
python manage.py djadmin_inspect --model myapp.MyModel
# 2. Look at REQUESTED FEATURES section
# - Feature not listed? → Configure it in ModelAdmin
# - Shows "NOT PROVIDED"? → Install the plugin
# 3. Check plugin contributions
# Look at "Mixins" section to see what plugins are active
Adding a New Plugin¶
When installing a third-party plugin:
# 1. Install the plugin
pip install djadmin-my-plugin
# 2. That's it! djadmin_apps() discovers it automatically
# No INSTALLED_APPS changes needed
# 3. Verify plugin is active
python manage.py djadmin_inspect --format json | grep plugin_name
Writing Comprehensive Tests¶
When testing a ModelAdmin:
# 1. Create basic test class
class TestMyAdmin(BaseCRUDTestCase):
model = MyModel
model_factory_class = MyModelFactory
# 2. Customize for specific tests
to_update_fields = {'name': 'Updated'}
def assert_create_successful(self, response, data):
# Custom assertions after create
assert MyModel.objects.filter(name=data['name']).exists()
# 3. Run tests
pytest tests/test_my_admin.py -v
Architecture¶
Plugin-Based Testing¶
Tests methods come from plugins via the djadmin_get_test_methods() hook:
# Core plugin provides default test methods
@hookimpl
def djadmin_get_test_methods():
return {
ListActionMixin: {'_test_list': test_list_method},
CreateViewActionMixin: {'_test_create_get': test_create_get_method},
# ... more test methods
}
# Other plugins can override with Replace()
@hookimpl
def djadmin_get_test_methods():
from djadmin.plugins.modifiers import Replace
return {
CreateViewActionMixin: {
Replace('_test_create_post', custom_create_post_test)
}
}
Benefits: - Plugins control how their features are tested - Core provides sensible defaults - No if/else logic in test infrastructure
Plugin Discovery¶
Third-party plugins declare themselves via entrypoints:
# Third-party plugin's pyproject.toml
[project.entry-points.djadmin]
my_plugin = "my_plugin.djadmin_hooks"
Built-in plugins (core, theme) are explicitly registered in djadmin_apps().
Discovery process:
1. djadmin_apps() is called from settings.py (before Django starts)
2. Built-in plugins registered explicitly
3. Third-party plugins discovered via pm.load_setuptools_entrypoints('djadmin')
4. All plugins call djadmin_get_required_apps() hook
5. Apps ordered using First, Before, After, Position modifiers
Ordering Resolution¶
Apps are categorized into buckets for ordering:
first = [] # Absolute first (themes)
before = [] # Before core apps
default = [] # Normal apps (includes djadmin core)
after = [] # After core apps
position = [] # Relative positioning
# Final order
result = first + before + default + after
# Then insert Position apps relative to others
Testing Strategy¶
Unit vs Integration Tests¶
Unit tests (test tool itself):
- tests/test_management_commands.py - djadmin_inspect tests
- tests/test_crud_testing.py - BaseCRUDTestCase tests
- tests/test_plugin_apps.py - djadmin_apps() tests
Integration tests (using webshop):
- examples/webshop/tests/test_admin_site.py - Real admin testing
- examples/webshop/tests/test_model_admin.py - Real configuration
Test Coverage¶
Current coverage:
- djadmin_inspect.py: 81%
- djadmin/testing.py: 98%
- djadmin/apps.py: 78%
- djadmin/plugins/modifiers.py: 100%
Target: 90%+ across all DX tools
Best Practices¶
Using djadmin_inspect¶
# DO: Use for debugging
python manage.py djadmin_inspect --model myapp.MyModel
# DO: Export for documentation
python manage.py djadmin_inspect > docs/admin_config.txt
# DO: Use JSON for automation
python manage.py djadmin_inspect --format json | jq '.features'
# DON'T: Use in production code (development/debugging only)
Using BaseCRUDTestCase¶
# DO: Only specify what you're testing
class TestProductAdmin(BaseCRUDTestCase):
model = Product
model_factory_class = ProductFactory
to_update_fields = {'name': 'Updated'} # Only testing name
# DO: Use customization hooks
def assert_create_successful(self, response, data):
assert Product.objects.filter(name=data['name']).exists()
# DON'T: Override _test_* methods directly
# Instead, use plugin hooks to customize test behavior
Using djadmin_apps()¶
# DO: Call at end of INSTALLED_APPS
INSTALLED_APPS = [
'django.contrib.admin',
# ... your apps
] + djadmin_apps()
# DO: Let plugins declare dependencies
# (In your plugin's djadmin_hooks.py)
@hookimpl
def djadmin_get_required_apps():
return [Before('my_plugin'), 'dependency']
# DON'T: Manually add plugin apps to INSTALLED_APPS
# Let djadmin_apps() handle it
Performance¶
djadmin_inspect¶
- Typical admin: <1 second
- Large admin (20+ actions): ~2 seconds
- JSON output: Same performance, but easier to parse
Optimization: Results are generated fresh each run (no caching). This is intentional for accuracy during development.
BaseCRUDTestCase¶
- Per-admin overhead: ~100ms
- Per-test overhead: ~50ms
- With FormCollection: ~100ms per POST test (JSON serialization)
Optimization: Tests run in parallel with pytest-xdist. Use pytest -n auto for best performance.
djadmin_apps()¶
- Discovery: <50ms (loads entrypoints)
- Ordering: <50ms (topological sort)
- Total: <100ms (runs once at Django startup)
Optimization: Results are not cached - function runs once during settings import, which is fine.
Troubleshooting¶
djadmin_inspect Issues¶
"No admins found"
- Check ModelAdmin is registered: site.register(Model, Admin)
- Verify model path: app_label.ModelName
- Ensure djadmin.py is imported
AttributeError/KeyError
- This is a bug - please report with traceback
- Workaround: Use --format json to get partial results
BaseCRUDTestCase Issues¶
422 status code on POST
- Expected with FormCollection (hierarchical JSON required)
- Plugin should override _test_create_post with custom test method
- See CRUD Testing Guide
DynamicURLConf pattern required - Use when registering/unregistering same model multiple times - See CRUD Testing Guide
djadmin_apps() Issues¶
ImproperlyConfigured: Could not position app
- Circular dependency or impossible constraint
- Check Position() modifiers in plugin hooks
- Use First, Before, After instead when possible
Plugin not discovered
- Verify entrypoint in pyproject.toml: [project.entry-points.djadmin]
- Check plugin module has djadmin_hooks.py
- Ensure plugin is installed: pip list | grep djadmin
Extending DX Tools¶
Custom Test Methods¶
To provide custom test methods for your plugin:
# your_plugin/djadmin_hooks.py
from djadmin.plugins import hookimpl
from djadmin.plugins.modifiers import Replace
@hookimpl
def djadmin_get_test_methods():
def custom_create_post(test_case, action):
# Custom test logic for your plugin's forms
pass
return {
CreateViewActionMixin: {
Replace('_test_create_post', custom_create_post)
}
}
Custom Ordering Modifiers¶
For complex app ordering:
# your_plugin/djadmin_hooks.py
from djadmin.plugins import hookimpl
from djadmin.plugins.modifiers import Position, Before
@hookimpl
def djadmin_get_required_apps():
return [
Position('your_app', after='some_app'), # Relative positioning
Before('theme_app'), # Before bucket
'dependency_app', # No constraint
]
Custom Introspection Output¶
Add custom sections to djadmin_inspect output:
Future Enhancements¶
Planned for Milestone 6 (Quality & Polish):
- Scaffolding CLI: Generate ModelAdmin boilerplate
- View Caching: Cache generated view classes
- Profiling Tools: Performance profiling utilities
- Enhanced djadmin_inspect: Better tree view, more filtering options
- Test Utilities: More helper functions for common test patterns
See Milestone 6 in PRD for details.
Contributing¶
When contributing to DX tools:
- Add tests for all new functionality (target 90%+ coverage)
- Update documentation with examples
- Consider plugin extensibility - can plugins customize this?
- Performance matters - DX tools run frequently during development