django-admin-deux 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- django_admin_deux-0.1.0/PKG-INFO +584 -0
- django_admin_deux-0.1.0/README.md +550 -0
- django_admin_deux-0.1.0/djadmin/__init__.py +43 -0
- django_admin_deux-0.1.0/djadmin/actions/__init__.py +46 -0
- django_admin_deux-0.1.0/djadmin/actions/base.py +634 -0
- django_admin_deux-0.1.0/djadmin/actions/dashboard.py +234 -0
- django_admin_deux-0.1.0/djadmin/actions/list_view.py +130 -0
- django_admin_deux-0.1.0/djadmin/actions/view_mixins.py +350 -0
- django_admin_deux-0.1.0/djadmin/apps.py +219 -0
- django_admin_deux-0.1.0/djadmin/conf.py +2 -0
- django_admin_deux-0.1.0/djadmin/dataclasses.py +541 -0
- django_admin_deux-0.1.0/djadmin/decorators.py +35 -0
- django_admin_deux-0.1.0/djadmin/factories/__init__.py +5 -0
- django_admin_deux-0.1.0/djadmin/factories/base.py +310 -0
- django_admin_deux-0.1.0/djadmin/forms.py +246 -0
- django_admin_deux-0.1.0/djadmin/layout.py +651 -0
- django_admin_deux-0.1.0/djadmin/management/__init__.py +1 -0
- django_admin_deux-0.1.0/djadmin/management/commands/__init__.py +1 -0
- django_admin_deux-0.1.0/djadmin/management/commands/djadmin_inspect.py +483 -0
- django_admin_deux-0.1.0/djadmin/management/commands/djadmin_list_apps.py +65 -0
- django_admin_deux-0.1.0/djadmin/options.py +440 -0
- django_admin_deux-0.1.0/djadmin/plugins/__init__.py +617 -0
- django_admin_deux-0.1.0/djadmin/plugins/core/__init__.py +1 -0
- django_admin_deux-0.1.0/djadmin/plugins/core/actions.py +309 -0
- django_admin_deux-0.1.0/djadmin/plugins/core/djadmin_hooks.py +382 -0
- django_admin_deux-0.1.0/djadmin/plugins/core/mixins.py +469 -0
- django_admin_deux-0.1.0/djadmin/plugins/core/utils.py +15 -0
- django_admin_deux-0.1.0/djadmin/plugins/modifiers.py +372 -0
- django_admin_deux-0.1.0/djadmin/plugins/permissions/__init__.py +25 -0
- django_admin_deux-0.1.0/djadmin/plugins/permissions/apps.py +12 -0
- django_admin_deux-0.1.0/djadmin/plugins/permissions/djadmin_hooks.py +50 -0
- django_admin_deux-0.1.0/djadmin/plugins/permissions/mixins.py +65 -0
- django_admin_deux-0.1.0/djadmin/plugins/permissions/operators.py +165 -0
- django_admin_deux-0.1.0/djadmin/plugins/permissions/permissions.py +233 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/__init__.py +1 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/apps.py +7 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/djadmin_hooks.py +44 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/static/djadmin/theme/css/theme.css +1 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/static/djadmin/theme/js/admin.js +214 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/templates/djadmin/_navigation.html +50 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/templates/djadmin/app_dashboard.html +48 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/templates/djadmin/project_dashboard.html +57 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/templates/registration/logged_out.html +29 -0
- django_admin_deux-0.1.0/djadmin/plugins/theme/templates/registration/login.html +64 -0
- django_admin_deux-0.1.0/djadmin/sites.py +281 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/_navigation.html +37 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/actions/_detail_item.html +14 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/actions/add.html +14 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/actions/confirm_bulk_delete.html +25 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/actions/confirm_delete.html +20 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/actions/detail.html +72 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/actions/edit.html +29 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/actions/form.html +22 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/admin_base.html +120 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/app_dashboard.html +54 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/base_action.html +15 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/eye.html +4 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/funnel.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/magnifying-glass.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/menu.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/moon.html +4 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/pencil.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/plus.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/sort-down.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/sort-up.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/sort.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/sun.html +4 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/icons/trash.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/_column_header_icons.html +1 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/_form.html +27 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/_icon.html +2 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/_sidebar.html +10 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/form_collection_warning.html +8 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/form_field.html +20 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/form_fieldset.html +16 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/form_layout.html +8 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/form_row.html +8 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/form_unknown_component.html +8 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/layout_item.html +3 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/includes/search_widget.html +33 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/model_list.html +277 -0
- django_admin_deux-0.1.0/djadmin/templates/djadmin/project_dashboard.html +56 -0
- django_admin_deux-0.1.0/djadmin/templatetags/__init__.py +1 -0
- django_admin_deux-0.1.0/djadmin/templatetags/djadmin_layout.py +150 -0
- django_admin_deux-0.1.0/djadmin/templatetags/djadmin_tags.py +254 -0
- django_admin_deux-0.1.0/djadmin/testing.py +453 -0
- django_admin_deux-0.1.0/djadmin/views.py +103 -0
- django_admin_deux-0.1.0/django_admin_deux.egg-info/PKG-INFO +584 -0
- django_admin_deux-0.1.0/django_admin_deux.egg-info/SOURCES.txt +129 -0
- django_admin_deux-0.1.0/django_admin_deux.egg-info/dependency_links.txt +1 -0
- django_admin_deux-0.1.0/django_admin_deux.egg-info/requires.txt +17 -0
- django_admin_deux-0.1.0/django_admin_deux.egg-info/top_level.txt +1 -0
- django_admin_deux-0.1.0/pyproject.toml +139 -0
- django_admin_deux-0.1.0/setup.cfg +4 -0
- django_admin_deux-0.1.0/tests/test_action_edge_cases.py +188 -0
- django_admin_deux-0.1.0/tests/test_action_errors.py +226 -0
- django_admin_deux-0.1.0/tests/test_action_filtering.py +463 -0
- django_admin_deux-0.1.0/tests/test_action_instantiation.py +169 -0
- django_admin_deux-0.1.0/tests/test_action_integration.py +258 -0
- django_admin_deux-0.1.0/tests/test_action_permissions.py +101 -0
- django_admin_deux-0.1.0/tests/test_action_urls.py +213 -0
- django_admin_deux-0.1.0/tests/test_actions_base.py +359 -0
- django_admin_deux-0.1.0/tests/test_adminsite_errors.py +125 -0
- django_admin_deux-0.1.0/tests/test_breadcrumbs.py +168 -0
- django_admin_deux-0.1.0/tests/test_column.py +316 -0
- django_admin_deux-0.1.0/tests/test_crud_testing.py +480 -0
- django_admin_deux-0.1.0/tests/test_dashboard_edge_cases.py +171 -0
- django_admin_deux-0.1.0/tests/test_dashboard_filtering.py +388 -0
- django_admin_deux-0.1.0/tests/test_feature_advertising.py +297 -0
- django_admin_deux-0.1.0/tests/test_feature_validation.py +171 -0
- django_admin_deux-0.1.0/tests/test_form_builder_core.py +531 -0
- django_admin_deux-0.1.0/tests/test_integration.py +279 -0
- django_admin_deux-0.1.0/tests/test_layout_components.py +675 -0
- django_admin_deux-0.1.0/tests/test_layout_display_rendering.py +224 -0
- django_admin_deux-0.1.0/tests/test_layout_template_tags.py +225 -0
- django_admin_deux-0.1.0/tests/test_layout_validation.py +379 -0
- django_admin_deux-0.1.0/tests/test_management_commands.py +180 -0
- django_admin_deux-0.1.0/tests/test_metaclass.py +420 -0
- django_admin_deux-0.1.0/tests/test_model_admin_permissions.py +122 -0
- django_admin_deux-0.1.0/tests/test_modeladmin_edge_cases.py +239 -0
- django_admin_deux-0.1.0/tests/test_pagination.py +214 -0
- django_admin_deux-0.1.0/tests/test_plugin_apps.py +86 -0
- django_admin_deux-0.1.0/tests/test_plugins.py +55 -0
- django_admin_deux-0.1.0/tests/test_query_parameter_preservation.py +282 -0
- django_admin_deux-0.1.0/tests/test_search.py +334 -0
- django_admin_deux-0.1.0/tests/test_sidebar_widgets.py +298 -0
- django_admin_deux-0.1.0/tests/test_template_tags.py +88 -0
- django_admin_deux-0.1.0/tests/test_urls.py +429 -0
- django_admin_deux-0.1.0/tests/test_view_factory.py +356 -0
- django_admin_deux-0.1.0/tests/test_viewfactory_errors.py +151 -0
- django_admin_deux-0.1.0/tests/test_webshop_plugin.py +178 -0
|
@@ -0,0 +1,584 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: django-admin-deux
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A modern, extensible replacement for Django admin
|
|
5
|
+
Author-email: Emma Delescolle <dev@levit.be>
|
|
6
|
+
Project-URL: Repository, https://gitlab.levitnet.be/levit/django-admin-deux/-/tree/main
|
|
7
|
+
Project-URL: Documentation, https://gitlab.levitnet.be/levit/django-admin-deux/-/tree/main/docs
|
|
8
|
+
Project-URL: Homepage, https://gitlab.levitnet.be/levit/django-admin-deux/-/tree/main/docs
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Framework :: Django
|
|
11
|
+
Classifier: Framework :: Django :: 5.2
|
|
12
|
+
Classifier: Framework :: Django :: 6.0
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Requires-Python: >=3.11
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
Requires-Dist: Django>=5.2
|
|
20
|
+
Requires-Dist: djp>=0.1.0
|
|
21
|
+
Provides-Extra: full
|
|
22
|
+
Requires-Dist: djadmin-formset>=0.1.0; extra == "full"
|
|
23
|
+
Requires-Dist: djadmin-filters>=0.1.0; extra == "full"
|
|
24
|
+
Provides-Extra: dev
|
|
25
|
+
Requires-Dist: pytest>=7.0; extra == "dev"
|
|
26
|
+
Requires-Dist: pytest-django>=4.0; extra == "dev"
|
|
27
|
+
Requires-Dist: pytest-cov>=4.0; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest-factoryboy>=2.6.0; extra == "dev"
|
|
29
|
+
Requires-Dist: factory-boy>=3.3.0; extra == "dev"
|
|
30
|
+
Requires-Dist: faker>=22.0.0; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.8.0; extra == "dev"
|
|
32
|
+
Requires-Dist: djlint>=1.34.0; extra == "dev"
|
|
33
|
+
Requires-Dist: pre-commit>=3.0.0; extra == "dev"
|
|
34
|
+
|
|
35
|
+
# django-admin-deux
|
|
36
|
+
|
|
37
|
+
A modern, extensible replacement for Django's admin interface, built on factory patterns and a robust plugin system.
|
|
38
|
+
|
|
39
|
+
[](https://www.python.org/downloads/)
|
|
40
|
+
[](https://www.djangoproject.com/)
|
|
41
|
+
[](LICENSE)
|
|
42
|
+
|
|
43
|
+
## Overview
|
|
44
|
+
|
|
45
|
+
**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.
|
|
46
|
+
|
|
47
|
+
### Key Features
|
|
48
|
+
|
|
49
|
+
- **Plugin Architecture**: Built on [djp](https://github.com/simonw/djp), allowing easy extension and customization
|
|
50
|
+
- **Factory Pattern**: Views are generated dynamically, enabling composition over inheritance
|
|
51
|
+
- **Feature Advertising**: Fail-fast validation ensures plugins provide requested features
|
|
52
|
+
- **Modern UI**: Tailwind-based default theme (coming in Milestone 4)
|
|
53
|
+
- **Familiar API**: If you know Django admin, you'll feel right at home
|
|
54
|
+
- **Incremental Adoption**: Can coexist with Django's built-in admin
|
|
55
|
+
|
|
56
|
+
### Current Status
|
|
57
|
+
|
|
58
|
+
🎉 **Milestone 5 Phase 2.7 Complete** - Permissions & Authorization System
|
|
59
|
+
|
|
60
|
+
Major milestones completed:
|
|
61
|
+
- ✅ **Milestone 1**: Foundation (Plugin system, AdminSite, URL routing, Feature validation)
|
|
62
|
+
- ✅ **Milestone 2**: Django-Filter Plugin (Filtering, ordering, search via djadmin-filters)
|
|
63
|
+
- ✅ **Milestone 3**: Layout API & Django-Formset Integration (Forms, inline editing via djadmin-formset)
|
|
64
|
+
- ✅ **Milestone 4**: Developer Experience (djadmin_inspect, BaseCRUDTestCase, djadmin_apps)
|
|
65
|
+
- ✅ **Milestone 5 Phase 2.7**: Permissions System (Core permissions, action filtering, ViewRecordAction)
|
|
66
|
+
|
|
67
|
+
**Test Results**: 720 passing tests, 82% coverage
|
|
68
|
+
**Plugins Available**: djadmin-formset, djadmin-filters
|
|
69
|
+
**Django Support**: 5.2, 6.0
|
|
70
|
+
**Python Support**: 3.11, 3.12, 3.13, 3.14
|
|
71
|
+
|
|
72
|
+
## Quick Start
|
|
73
|
+
|
|
74
|
+
### Installation
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
# Basic installation
|
|
78
|
+
pip install django-admin-deux
|
|
79
|
+
|
|
80
|
+
# With all plugins (djadmin-formset + djadmin-filters)
|
|
81
|
+
pip install django-admin-deux[full]
|
|
82
|
+
|
|
83
|
+
# With specific plugins
|
|
84
|
+
pip install django-admin-deux[formset] # Just djadmin-formset
|
|
85
|
+
pip install django-admin-deux[filters] # Just djadmin-filters
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Basic Usage
|
|
89
|
+
|
|
90
|
+
**1. Add to your `INSTALLED_APPS`:**
|
|
91
|
+
|
|
92
|
+
```python
|
|
93
|
+
# settings.py
|
|
94
|
+
INSTALLED_APPS = [
|
|
95
|
+
# ...
|
|
96
|
+
'djadmin',
|
|
97
|
+
# ...
|
|
98
|
+
]
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
**2. Create a `djadmin.py` file in your app:**
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
# myapp/djadmin.py
|
|
105
|
+
from djadmin import ModelAdmin, register, Layout, Field, Fieldset, Row
|
|
106
|
+
from .models import Book
|
|
107
|
+
|
|
108
|
+
@register(Book)
|
|
109
|
+
class BookAdmin(ModelAdmin):
|
|
110
|
+
list_display = ['title', 'author', 'published_date']
|
|
111
|
+
search_fields = ['title', 'author']
|
|
112
|
+
list_filter = ['published_date']
|
|
113
|
+
|
|
114
|
+
# Optional: Customize form layout
|
|
115
|
+
layout = Layout(
|
|
116
|
+
Fieldset('Book Information',
|
|
117
|
+
Field('title'),
|
|
118
|
+
Row(
|
|
119
|
+
Field('author', css_classes=['flex-1', 'pr-2']),
|
|
120
|
+
Field('published_date', css_classes=['flex-1', 'pl-2']),
|
|
121
|
+
),
|
|
122
|
+
),
|
|
123
|
+
Fieldset('Content',
|
|
124
|
+
Field('description', widget='textarea', attrs={'rows': 6}),
|
|
125
|
+
),
|
|
126
|
+
)
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**3. Add to your URLs:**
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
# urls.py
|
|
133
|
+
from django.urls import path, include
|
|
134
|
+
from djadmin import site
|
|
135
|
+
|
|
136
|
+
urlpatterns = [
|
|
137
|
+
path('admin/', admin.site.urls), # Django's admin (optional)
|
|
138
|
+
path('djadmin/', include((site.urls, 'djadmin'), namespace='djadmin')),
|
|
139
|
+
]
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
**4. Visit your admin:**
|
|
143
|
+
|
|
144
|
+
```
|
|
145
|
+
http://localhost:8000/djadmin/
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Development Setup
|
|
149
|
+
|
|
150
|
+
### Prerequisites
|
|
151
|
+
|
|
152
|
+
- Python 3.11 or higher
|
|
153
|
+
- [just](https://github.com/casey/just) command runner (optional but recommended)
|
|
154
|
+
- Git
|
|
155
|
+
|
|
156
|
+
### Clone and Setup
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
# Clone the repository
|
|
160
|
+
git clone https://github.com/yourusername/django-admin-deux.git
|
|
161
|
+
cd django-admin-deux
|
|
162
|
+
|
|
163
|
+
# Run the setup script
|
|
164
|
+
./setup_dev.sh
|
|
165
|
+
|
|
166
|
+
# Or manually:
|
|
167
|
+
python -m venv .venv
|
|
168
|
+
source .venv/bin/activate # On Windows: .venv\Scripts\activate
|
|
169
|
+
pip install -e ".[dev]"
|
|
170
|
+
pre-commit install
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
### Development Commands
|
|
174
|
+
|
|
175
|
+
With `just` installed:
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Run tests
|
|
179
|
+
just test
|
|
180
|
+
|
|
181
|
+
# Run tests with coverage
|
|
182
|
+
just test-coverage
|
|
183
|
+
|
|
184
|
+
# Format code
|
|
185
|
+
just format
|
|
186
|
+
|
|
187
|
+
# Run linters
|
|
188
|
+
just lint
|
|
189
|
+
|
|
190
|
+
# Run development server
|
|
191
|
+
just runserver
|
|
192
|
+
|
|
193
|
+
# See all commands
|
|
194
|
+
just --list
|
|
195
|
+
```
|
|
196
|
+
|
|
197
|
+
Without `just`:
|
|
198
|
+
|
|
199
|
+
```bash
|
|
200
|
+
# Run tests
|
|
201
|
+
pytest
|
|
202
|
+
|
|
203
|
+
# Run tests with coverage
|
|
204
|
+
pytest --cov=djadmin --cov-report=html
|
|
205
|
+
|
|
206
|
+
# Format code
|
|
207
|
+
ruff format .
|
|
208
|
+
ruff check . --fix
|
|
209
|
+
djlint djadmin/ --reformat
|
|
210
|
+
|
|
211
|
+
# Run linters
|
|
212
|
+
ruff check .
|
|
213
|
+
djlint djadmin/ --lint
|
|
214
|
+
|
|
215
|
+
# Run development server
|
|
216
|
+
cd tests && python manage.py runserver
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
### Project Structure
|
|
220
|
+
|
|
221
|
+
```
|
|
222
|
+
django-admin-deux/
|
|
223
|
+
├── djadmin/ # Main package
|
|
224
|
+
│ ├── plugins/ # Plugin system
|
|
225
|
+
│ ├── sites.py # AdminSite class
|
|
226
|
+
│ ├── options.py # ModelAdmin class
|
|
227
|
+
│ └── decorators.py # @register decorator
|
|
228
|
+
├── examples/
|
|
229
|
+
│ └── webshop/ # Example e-commerce app
|
|
230
|
+
├── tests/ # Test infrastructure
|
|
231
|
+
├── pyproject.toml # Package configuration
|
|
232
|
+
└── tox.ini # CI test matrix
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
## Key Features
|
|
236
|
+
|
|
237
|
+
## Architecture
|
|
238
|
+
|
|
239
|
+
### Plugin System
|
|
240
|
+
|
|
241
|
+
django-admin-deux uses [djp](https://github.com/simonw/djp) for its plugin architecture. Plugins can:
|
|
242
|
+
|
|
243
|
+
- Add mixins to views
|
|
244
|
+
- Provide default actions
|
|
245
|
+
- Modify querysets
|
|
246
|
+
- Add context data
|
|
247
|
+
- Provide CSS/JS assets
|
|
248
|
+
|
|
249
|
+
**Example plugin:**
|
|
250
|
+
|
|
251
|
+
```python
|
|
252
|
+
# myapp/djadmin_hooks.py
|
|
253
|
+
from djadmin.plugins import hookimpl
|
|
254
|
+
|
|
255
|
+
@hookimpl
|
|
256
|
+
def djadmin_provides_features():
|
|
257
|
+
"""Advertise features this plugin provides"""
|
|
258
|
+
return ['search', 'filter']
|
|
259
|
+
|
|
260
|
+
@hookimpl
|
|
261
|
+
def djadmin_get_list_view_mixins():
|
|
262
|
+
"""Add search functionality to ListView"""
|
|
263
|
+
from .mixins import SearchMixin
|
|
264
|
+
return [SearchMixin]
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### Feature Advertising
|
|
268
|
+
|
|
269
|
+
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:
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
class BookAdmin(ModelAdmin):
|
|
273
|
+
search_fields = ['title'] # Requires 'search' feature
|
|
274
|
+
|
|
275
|
+
# If no plugin provides 'search':
|
|
276
|
+
# ImproperlyConfigured: ModelAdmin BookAdmin requests features {'search'}
|
|
277
|
+
# but no registered plugin provides them.
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### View Factory Pattern
|
|
281
|
+
|
|
282
|
+
Views are generated dynamically using class-based factories, allowing plugins to inject mixins and customize behavior without complex inheritance chains.
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
# Conceptual example (Milestone 2)
|
|
286
|
+
class ListViewFactory:
|
|
287
|
+
def create_view(self, model, admin_class, plugins):
|
|
288
|
+
# Collect mixins from plugins
|
|
289
|
+
mixins = []
|
|
290
|
+
for plugin in plugins:
|
|
291
|
+
mixins.extend(plugin.get_list_view_mixins())
|
|
292
|
+
|
|
293
|
+
# Generate view class dynamically
|
|
294
|
+
view_class = type(
|
|
295
|
+
f'{model.__name__}ListView',
|
|
296
|
+
tuple(mixins + [BaseListView]),
|
|
297
|
+
{'model': model, 'admin': admin_class}
|
|
298
|
+
)
|
|
299
|
+
return view_class
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Layout API
|
|
303
|
+
|
|
304
|
+
django-admin-deux provides a powerful Layout API for customizing form layouts with progressive enhancement:
|
|
305
|
+
|
|
306
|
+
```python
|
|
307
|
+
from djadmin import ModelAdmin, register, Layout, Field, Fieldset, Row
|
|
308
|
+
|
|
309
|
+
@register(Author)
|
|
310
|
+
class AuthorAdmin(ModelAdmin):
|
|
311
|
+
layout = Layout(
|
|
312
|
+
Fieldset('Personal Information',
|
|
313
|
+
Row(
|
|
314
|
+
Field('first_name', css_classes=['flex-1', 'pr-2']),
|
|
315
|
+
Field('last_name', css_classes=['flex-1', 'pl-2']),
|
|
316
|
+
),
|
|
317
|
+
Field('birth_date', label='Date of Birth'),
|
|
318
|
+
),
|
|
319
|
+
Fieldset('Biography',
|
|
320
|
+
Field('bio', widget='textarea', attrs={'rows': 8}),
|
|
321
|
+
),
|
|
322
|
+
)
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
**Action-Specific Layouts**:
|
|
326
|
+
|
|
327
|
+
Use different layouts for create vs update actions (follows the same pattern as `create_form_class`/`update_form_class`):
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
@register(Product)
|
|
331
|
+
class ProductAdmin(ModelAdmin):
|
|
332
|
+
# Create-specific layout (simpler, focused on essentials)
|
|
333
|
+
create_layout = Layout(
|
|
334
|
+
Fieldset('New Product',
|
|
335
|
+
Field('name', required=True),
|
|
336
|
+
Row(
|
|
337
|
+
Field('price', css_classes=['flex-1']),
|
|
338
|
+
Field('cost', css_classes=['flex-1']),
|
|
339
|
+
),
|
|
340
|
+
),
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
# Update-specific layout (includes metadata fields)
|
|
344
|
+
update_layout = Layout(
|
|
345
|
+
Fieldset('Product Information',
|
|
346
|
+
Field('name'),
|
|
347
|
+
Field('description', widget='textarea'),
|
|
348
|
+
),
|
|
349
|
+
Fieldset('Metadata',
|
|
350
|
+
Field('created_at', widget=DateTimeInput(attrs={'readonly': True})),
|
|
351
|
+
Field('updated_at', widget=DateTimeInput(attrs={'readonly': True})),
|
|
352
|
+
),
|
|
353
|
+
)
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
**Core Features** (no plugin required):
|
|
357
|
+
- ✅ **Fieldsets** - Group fields with legends and descriptions
|
|
358
|
+
- ✅ **Rows** - Horizontal layouts using flexbox
|
|
359
|
+
- ✅ **Field Customization** - Labels, widgets, help text, CSS classes
|
|
360
|
+
- ✅ **Widget Shortcuts** - Use strings like `'textarea'`, `'email'`
|
|
361
|
+
- ✅ **Django Admin Migration** - Automatic conversion of `fieldsets`
|
|
362
|
+
- ✅ **Action-Specific Layouts** - Different layouts for create/update (`create_layout`, `update_layout`)
|
|
363
|
+
|
|
364
|
+
**Plugin Features** (with djadmin-formset):
|
|
365
|
+
- ✅ **Collections** - Inline editing of related objects
|
|
366
|
+
- ✅ **Conditional Fields** - Show/hide fields based on values
|
|
367
|
+
- ✅ **Computed Fields** - Auto-calculate values
|
|
368
|
+
- ✅ **Client-side Validation** - Instant feedback
|
|
369
|
+
- ✅ **Drag-and-drop** - Reorder inline items
|
|
370
|
+
|
|
371
|
+
**Learn more**: [Layout API Documentation](docs/layout-api/index.md)
|
|
372
|
+
|
|
373
|
+
## Testing
|
|
374
|
+
|
|
375
|
+
The project uses pytest with extensive test coverage:
|
|
376
|
+
|
|
377
|
+
```bash
|
|
378
|
+
# Run all tests
|
|
379
|
+
just test
|
|
380
|
+
|
|
381
|
+
# Run specific test
|
|
382
|
+
just test-file tests/test_plugins.py
|
|
383
|
+
|
|
384
|
+
# Run tests matching pattern
|
|
385
|
+
just test-match test_register
|
|
386
|
+
|
|
387
|
+
# Run with coverage report
|
|
388
|
+
just test-coverage
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
### Test Organization
|
|
392
|
+
|
|
393
|
+
- **`tests/`** - Infrastructure tests (plugins, URLs, validation)
|
|
394
|
+
- **`examples/webshop/tests/`** - Integration tests using example models
|
|
395
|
+
|
|
396
|
+
### Test Best Practices: Avoiding Test Pollution
|
|
397
|
+
|
|
398
|
+
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):
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
from django.test import TestCase, override_settings
|
|
402
|
+
from django.urls import clear_url_caches
|
|
403
|
+
from djadmin import ModelAdmin, site
|
|
404
|
+
|
|
405
|
+
# Dynamic URLconf regenerates on each access
|
|
406
|
+
class DynamicURLConf:
|
|
407
|
+
@property
|
|
408
|
+
def urlpatterns(self):
|
|
409
|
+
from django.urls import path, include
|
|
410
|
+
return [path('djadmin/', include(site.urls))]
|
|
411
|
+
|
|
412
|
+
@override_settings(ROOT_URLCONF=DynamicURLConf())
|
|
413
|
+
class TestMyFeature(TestCase):
|
|
414
|
+
def setUp(self):
|
|
415
|
+
# Clean registry before test
|
|
416
|
+
if MyModel in site._registry:
|
|
417
|
+
site.unregister(MyModel)
|
|
418
|
+
self.my_objects = MyModelFactory.create_batch(5)
|
|
419
|
+
|
|
420
|
+
def tearDown(self):
|
|
421
|
+
# Clean registry and caches after test
|
|
422
|
+
if MyModel in site._registry:
|
|
423
|
+
site.unregister(MyModel)
|
|
424
|
+
clear_url_caches()
|
|
425
|
+
if hasattr(self.client, '_cached_urlconf'):
|
|
426
|
+
delattr(self.client, '_cached_urlconf')
|
|
427
|
+
|
|
428
|
+
def test_my_feature(self):
|
|
429
|
+
# Register with override=True
|
|
430
|
+
class MyModelAdmin(ModelAdmin):
|
|
431
|
+
list_display = ['field1', 'field2']
|
|
432
|
+
site.register(MyModel, MyModelAdmin, override=True)
|
|
433
|
+
# ... test code
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
**Why this is needed**: Django caches URL patterns and view closures, so without this pattern, tests will use stale admin configurations from previous tests.
|
|
437
|
+
|
|
438
|
+
**Reference**: This pattern is based on https://ጮ.cc/2019/11/09/django-testing-dynamic-urlconf.html
|
|
439
|
+
|
|
440
|
+
See `tests/test_search.py` for a complete example.
|
|
441
|
+
|
|
442
|
+
### Example Models
|
|
443
|
+
|
|
444
|
+
The webshop example app provides realistic test data:
|
|
445
|
+
|
|
446
|
+
- `Category` - Hierarchical product categories
|
|
447
|
+
- `Product` - Products with SKU, pricing, stock
|
|
448
|
+
- `Customer` - Customer accounts with addresses
|
|
449
|
+
- `Order` - Orders with status tracking
|
|
450
|
+
- `OrderItem` - Line items in orders
|
|
451
|
+
- `Review` - Product reviews with ratings
|
|
452
|
+
- `Tag` - Tags (many-to-many relationship example)
|
|
453
|
+
|
|
454
|
+
All models have corresponding factory_boy factories for easy test data creation.
|
|
455
|
+
|
|
456
|
+
## Continuous Integration
|
|
457
|
+
|
|
458
|
+
The project uses GitLab CI with tox to test all Python/Django combinations:
|
|
459
|
+
|
|
460
|
+
- Python: 3.11, 3.12, 3.13, 3.14
|
|
461
|
+
- Django: 5.2, 6.0
|
|
462
|
+
|
|
463
|
+
Pre-commit hooks run on every commit to catch issues early:
|
|
464
|
+
- Ruff (linting and formatting)
|
|
465
|
+
- djLint (Django template linting)
|
|
466
|
+
- pytest (test suite)
|
|
467
|
+
|
|
468
|
+
## Roadmap
|
|
469
|
+
|
|
470
|
+
### Milestone 1: Foundation ✅ Complete
|
|
471
|
+
- ✅ Plugin system with djp
|
|
472
|
+
- ✅ AdminSite and ModelAdmin classes
|
|
473
|
+
- ✅ URL routing
|
|
474
|
+
- ✅ Feature validation
|
|
475
|
+
- ✅ Basic templates
|
|
476
|
+
|
|
477
|
+
### Milestone 2: View Factories & Actions ✅ Complete
|
|
478
|
+
- ✅ Factory pattern implementation
|
|
479
|
+
- ✅ ListView, CreateView, DetailView
|
|
480
|
+
- ✅ Form handling
|
|
481
|
+
- ✅ List actions (e.g., "Add New")
|
|
482
|
+
- ✅ Bulk actions (e.g., "Delete Selected")
|
|
483
|
+
- ✅ Record actions (e.g., "Edit", "Delete")
|
|
484
|
+
|
|
485
|
+
### Milestone 3: Layout API & Django-Formset Integration ✅ Complete
|
|
486
|
+
- ✅ Core Layout API (Field, Fieldset, Row, Collection)
|
|
487
|
+
- ✅ Automatic Django admin fieldsets conversion
|
|
488
|
+
- ✅ Feature advertising system
|
|
489
|
+
- ✅ Basic flexbox rendering
|
|
490
|
+
- ✅ FormFactory for django-formset integration
|
|
491
|
+
- ✅ Inline editing (Collections)
|
|
492
|
+
- ✅ Conditional fields (show_if/hide_if)
|
|
493
|
+
- ✅ Computed fields (calculate)
|
|
494
|
+
- ✅ 565 tests passing, 87% coverage
|
|
495
|
+
|
|
496
|
+
### Milestone 4: Developer Experience ✅ Complete
|
|
497
|
+
- ✅ djadmin_inspect management command
|
|
498
|
+
- ✅ BaseCRUDTestCase for automated testing
|
|
499
|
+
- ✅ Plugin-driven INSTALLED_APPS
|
|
500
|
+
- ✅ Comprehensive documentation
|
|
501
|
+
|
|
502
|
+
### Milestone 5: Permissions System (Current - Phase 2.7 Complete)
|
|
503
|
+
- ✅ Core permission classes (IsAuthenticated, IsStaff, HasDjangoPermission)
|
|
504
|
+
- ✅ Composition operators (AND/OR/NOT)
|
|
505
|
+
- ✅ ModelAdmin permission integration
|
|
506
|
+
- ✅ Action-level permissions
|
|
507
|
+
- ✅ Object-level permissions support
|
|
508
|
+
- ✅ Action filtering based on permissions
|
|
509
|
+
- ✅ ViewRecordAction for view-only users
|
|
510
|
+
- ✅ Dashboard filtering
|
|
511
|
+
- ✅ 720 tests passing, 82% coverage
|
|
512
|
+
- 📋 Next: Guardian plugin (optional), UI integration & polish
|
|
513
|
+
|
|
514
|
+
### Milestone 6: Quality & Polish (Planned)
|
|
515
|
+
- Coverage improvements (>90%)
|
|
516
|
+
- Accessibility audit (WCAG 2.1 AA)
|
|
517
|
+
- Performance benchmarking
|
|
518
|
+
- CI/CD infrastructure
|
|
519
|
+
- Production-ready release
|
|
520
|
+
|
|
521
|
+
## Contributing
|
|
522
|
+
|
|
523
|
+
We welcome contributions! The project is in early development, so there are many opportunities to help shape the future of Django admin interfaces.
|
|
524
|
+
|
|
525
|
+
### Development Workflow
|
|
526
|
+
|
|
527
|
+
1. **Fork and clone** the repository
|
|
528
|
+
2. **Create a feature branch**: `git checkout -b feature/my-feature`
|
|
529
|
+
3. **Make your changes** and add tests
|
|
530
|
+
4. **Run tests and linters**: `just test && just lint`
|
|
531
|
+
5. **Commit** (pre-commit hooks will run automatically)
|
|
532
|
+
6. **Push** and create a merge request
|
|
533
|
+
|
|
534
|
+
### Code Style
|
|
535
|
+
|
|
536
|
+
- **Python**: Ruff formatter (120 character line length)
|
|
537
|
+
- **Templates**: djLint formatter
|
|
538
|
+
- **Commits**: Use conventional commit format (e.g., `feat:`, `fix:`, `docs:`)
|
|
539
|
+
- **Tests**: Pytest with >80% coverage required
|
|
540
|
+
|
|
541
|
+
### Running Tests Locally
|
|
542
|
+
|
|
543
|
+
```bash
|
|
544
|
+
# Run full test suite
|
|
545
|
+
just test
|
|
546
|
+
|
|
547
|
+
# Test specific Python/Django combination
|
|
548
|
+
tox -e py311-django52
|
|
549
|
+
|
|
550
|
+
# Test all combinations (like CI)
|
|
551
|
+
tox
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
## Documentation
|
|
555
|
+
|
|
556
|
+
### User Documentation
|
|
557
|
+
- **[Layout API Overview](docs/layout-api/index.md)** - Introduction to the Layout API
|
|
558
|
+
- **[Component Reference](docs/layout-api/components.md)** - Detailed API for each component
|
|
559
|
+
- **[Django Admin Migration Guide](docs/layout-api/django-admin-migration.md)** - Migrate from Django admin
|
|
560
|
+
- **[Layout Examples](docs/layout-api/examples.md)** - Real-world usage patterns
|
|
561
|
+
|
|
562
|
+
### Developer Documentation
|
|
563
|
+
- **[CLAUDE.md](CLAUDE.md)** - Technical documentation for AI assistants
|
|
564
|
+
- **[PRD](claude_docs/django-admin-deux-PRD.md)** - Complete product requirements (v2.7)
|
|
565
|
+
- **[Milestone 5 Implementation Plan](claude_docs/milestone-5-implementation-plan.md)** - Current milestone plan
|
|
566
|
+
- **[CHANGELOG](CHANGELOG.md)** - Version history and release notes
|
|
567
|
+
|
|
568
|
+
## License
|
|
569
|
+
|
|
570
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
571
|
+
|
|
572
|
+
## Acknowledgments
|
|
573
|
+
|
|
574
|
+
- Inspired by [django-admin2](https://github.com/jazzband/django-admin2)
|
|
575
|
+
- Built with [djp](https://github.com/simonw/djp) by Simon Willison
|
|
576
|
+
- Uses [factory_boy](https://github.com/FactoryBoy/factory_boy) for test data
|
|
577
|
+
- Styled with [Tailwind CSS](https://tailwindcss.com/) (coming in Milestone 4)
|
|
578
|
+
|
|
579
|
+
---
|
|
580
|
+
|
|
581
|
+
**Status**: 🎉 Milestone 5 Phase 2.7 Complete
|
|
582
|
+
**Python**: 3.11+
|
|
583
|
+
**Django**: 5.2+
|
|
584
|
+
**License**: MIT
|