django-admin-superfilter 0.9.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.
@@ -0,0 +1,20 @@
1
+ # MIT LICENCE
2
+ Copyright (c) 2025 Omar BENHAMID
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining a copy
5
+ of this software and associated documentation files (the "Software"), to deal
6
+ in the Software without restriction, including without limitation the rights
7
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
8
+ copies of the Software, and to permit persons to whom the Software is
9
+ furnished to do so, subject to the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be included in all
12
+ copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
17
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
19
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
20
+ SOFTWARE.
@@ -0,0 +1,348 @@
1
+ Metadata-Version: 2.4
2
+ Name: django-admin-superfilter
3
+ Version: 0.9.0
4
+ Summary: TODO
5
+ License: MIT
6
+ License-File: LICENCE.md
7
+ Keywords: django,admin,filter,tools
8
+ Author: Omar BENHAMID
9
+ Author-email: omar.benhamid@smart-gts.com
10
+ Requires-Python: >=3.10
11
+ Classifier: Framework :: Django
12
+ Classifier: Framework :: Django :: 4
13
+ Classifier: Framework :: Django :: 5
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Requires-Dist: django (>=4.0)
17
+ Description-Content-Type: text/markdown
18
+
19
+ # django-admin-superfilter
20
+
21
+ Advanced, modern filtering for Django admin changelists, with saved filters and selectable columns.
22
+
23
+ ## Features
24
+
25
+ - Search-bar style filter UI injected into Django admin changelists
26
+ - Type-aware filtering for text, numeric, boolean, choice, date, datetime and relation fields
27
+ - Field path traversal such as `author__email`
28
+ - Saved filters per authenticated user
29
+ - Column selection and ordering persisted with saved filters
30
+ - AJAX-backed relation picker using Django admin Select2 assets
31
+ - Support for custom filter fields via `SuperFilterField`
32
+ - Single JSON query parameter for rules (`sf`) and one for visible columns (`sfc`)
33
+ - Works without templates overrides
34
+
35
+ ## Requirements
36
+
37
+ - Python 3.10+
38
+ - Django 4.0+
39
+
40
+ ## Installation
41
+
42
+ Install the package:
43
+
44
+ ```bash
45
+ pip install django-admin-superfilter
46
+ ```
47
+
48
+ Add the app to `INSTALLED_APPS`:
49
+
50
+ ```python
51
+ INSTALLED_APPS = [
52
+ # ...
53
+ "superfilter",
54
+ ]
55
+ ```
56
+
57
+ Run migrations:
58
+
59
+ ```bash
60
+ python manage.py migrate
61
+ ```
62
+
63
+ ## Quick start
64
+
65
+ ```python
66
+ from django.contrib import admin
67
+ from superfilter.admin import SuperFilterAdminMixin
68
+
69
+ from .models import Bird
70
+
71
+
72
+ @admin.register(Bird)
73
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
74
+ list_display = ("species", "location", "count")
75
+ search_fields = ("species",)
76
+ ```
77
+
78
+ Important:
79
+
80
+ - Put `SuperFilterAdminMixin` before `admin.ModelAdmin` in the MRO.
81
+ - By default, filterable fields come from `list_display`.
82
+ - Non-model entries in `list_display` are ignored.
83
+ - Traversed model fields like `location__city__name` are supported.
84
+
85
+ ## What appears in the UI
86
+
87
+ The package adds a search-bar-like control above the changelist with:
88
+
89
+ - an add-filter button
90
+ - filter badges for current rules
91
+ - an Apply button
92
+ - a split menu with Save
93
+ - a Reset button
94
+ - a Columns toggle button
95
+ - a collapsible column chooser
96
+ - saved filter chips
97
+
98
+ Saved filters store both:
99
+
100
+ - the active filter rules
101
+ - the selected column list and order
102
+
103
+ ## Supported field kinds and operators
104
+
105
+ ### All fields
106
+
107
+ - `set`
108
+ - `not_set`
109
+
110
+ ### Text-like fields
111
+
112
+ Text-like fields include `CharField`, `TextField`, `EmailField`, `SlugField`, `URLField`, `UUIDField`.
113
+
114
+ Operators:
115
+
116
+ - `set`
117
+ - `not_set`
118
+ - `eq`
119
+ - `neq`
120
+ - `contains`
121
+ - `not_contains`
122
+ - `in`
123
+ - `not_in`
124
+
125
+ ### Numeric fields
126
+
127
+ Operators:
128
+
129
+ - `set`
130
+ - `not_set`
131
+ - `eq`
132
+ - `neq`
133
+ - `gt`
134
+ - `lt`
135
+ - `gte`
136
+ - `lte`
137
+
138
+ ### Boolean fields
139
+
140
+ Operators:
141
+
142
+ - `set`
143
+ - `not_set`
144
+ - `true`
145
+ - `false`
146
+
147
+ ### Choice fields
148
+
149
+ Operators:
150
+
151
+ - `set`
152
+ - `not_set`
153
+ - `in`
154
+ - `not_in`
155
+
156
+ ### Foreign key / relation fields
157
+
158
+ Operators:
159
+
160
+ - `set`
161
+ - `not_set`
162
+ - `in`
163
+ - `not_in`
164
+
165
+ ### Date and datetime fields
166
+
167
+ Operators:
168
+
169
+ - `set`
170
+ - `not_set`
171
+ - `eq`
172
+ - `before`
173
+ - `after`
174
+ - `between`
175
+
176
+ ## Semantics
177
+
178
+ - `set` means the field is considered populated
179
+ - `not_set` means the field is empty
180
+ - For text fields, empty means `NULL` or empty string
181
+ - For boolean fields, `set` / `not_set` only target `NULL` vs non-`NULL`
182
+ - `contains` uses `icontains`
183
+ - `not_contains` negates `icontains`
184
+ - `between` on date/datetime expects exactly two values
185
+ - Relation filters use selected related object primary keys
186
+
187
+ ## Configuration
188
+
189
+ `SuperFilterAdminMixin` exposes a few attributes:
190
+
191
+ - `superfilter_param_name = "sf"`
192
+ - `superfilter_columns_param_name = "sfc"`
193
+ - `superfilter_fields = None`
194
+ - `superfilter_page_size = 25`
195
+ - `superfilter_all_limit = 2000`
196
+
197
+ Example:
198
+
199
+ ```python
200
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
201
+ list_display = ("species", "location", "count")
202
+ superfilter_page_size = 50
203
+ superfilter_all_limit = 5000
204
+ ```
205
+
206
+ ## Restricting filterable fields
207
+
208
+ By default, filterable fields are taken from `list_display`.
209
+
210
+ To expose a different set of fields, use `superfilter_fields`:
211
+
212
+ ```python
213
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
214
+ list_display = ("species", "location", "count")
215
+ superfilter_fields = ("species", "count", "location__city")
216
+ ```
217
+
218
+ This only affects filterable fields. Column selection still uses `list_display`.
219
+
220
+ ## Custom filter fields
221
+
222
+ You can plug in custom fields that do not map directly to a Django model field.
223
+
224
+ Create a subclass of `SuperFilterField`:
225
+
226
+ ```python
227
+ from django.db.models import Q
228
+ from superfilter.logic import SuperFilterField
229
+
230
+
231
+ class HasLargeCountField(SuperFilterField):
232
+ path = "has_large_count"
233
+ label = "Large count"
234
+ kind = "choice"
235
+ choices = [
236
+ {"value": "yes", "label": "Yes"},
237
+ {"value": "no", "label": "No"},
238
+ ]
239
+
240
+ def apply_rule(self, queryset, rule):
241
+ values = set(rule.get("value") or [])
242
+ if "yes" in values and "no" not in values:
243
+ return queryset.filter(count__gte=100)
244
+ if "no" in values and "yes" not in values:
245
+ return queryset.filter(count__lt=100)
246
+ return queryset
247
+ ```
248
+
249
+ Register it in `superfilter_fields`:
250
+
251
+ ```python
252
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
253
+ list_display = ("species", "location", "count")
254
+ superfilter_fields = ("species", HasLargeCountField)
255
+ ```
256
+
257
+ ## Saved filters
258
+
259
+ Saved filters are stored in the `SavedSuperFilter` model and are scoped by:
260
+
261
+ - user
262
+ - app label
263
+ - model name
264
+ - saved filter name
265
+
266
+ Notes:
267
+
268
+ - saving requires an authenticated user
269
+ - saved filters are private to the user
270
+ - the latest saved filter with column settings is used as fallback when `sfc` is absent from the querystring
271
+
272
+ ## URL format
273
+
274
+ Rules are sent in the `sf` query parameter as JSON:
275
+
276
+ ```json
277
+ [
278
+ {"field": "species", "op": "contains", "value": "owl"},
279
+ {"field": "location", "op": "in", "value": [1, 2]},
280
+ {"field": "count", "op": "gte", "value": 10}
281
+ ]
282
+ ```
283
+
284
+ Columns are sent in the `sfc` query parameter as JSON:
285
+
286
+ ```json
287
+ ["location", "species", "count"]
288
+ ```
289
+
290
+ ## Relation option loading
291
+
292
+ For relation filters, the package exposes an admin endpoint that:
293
+
294
+ - reuses the related admin's `get_search_results()` when available
295
+ - otherwise falls back to searching up to three text fields
296
+ - otherwise falls back to PK search for numeric terms
297
+
298
+ ## Static assets
299
+
300
+ The mixin injects:
301
+
302
+ - `admin/css/vendor/select2/select2.min.css`
303
+ - `admin/js/vendor/select2/select2.full.min.js`
304
+ - `superfilter/superfilter.css`
305
+ - `superfilter/superfilter.js`
306
+
307
+ No custom template override is required.
308
+
309
+ ## Limitations
310
+
311
+ - Filtering ignores callable/computed `list_display` entries unless implemented as `SuperFilterField`
312
+ - Column selection only works on entries present in `list_display`
313
+ - Saved filters depend on migrations for the `superfilter` app being applied
314
+ - The package ships with admin-focused frontend assets and is not intended for non-admin pages
315
+
316
+ ## Example project
317
+
318
+ A runnable sample project is available in:
319
+
320
+ - `examples/sampleapp/`
321
+
322
+ Run it with:
323
+
324
+ ```bash
325
+ cd examples/sampleapp
326
+ python manage.py migrate
327
+ python manage.py runserver
328
+ ```
329
+
330
+ ## Development
331
+
332
+ Run tests from the repository root:
333
+
334
+ ```bash
335
+ python manage.py test superfilter
336
+ ```
337
+
338
+ Or use the sample project:
339
+
340
+ ```bash
341
+ cd examples/sampleapp
342
+ python manage.py test
343
+ ```
344
+
345
+ ## License
346
+
347
+ MIT. See `LICENCE.md`.
348
+
@@ -0,0 +1,329 @@
1
+ # django-admin-superfilter
2
+
3
+ Advanced, modern filtering for Django admin changelists, with saved filters and selectable columns.
4
+
5
+ ## Features
6
+
7
+ - Search-bar style filter UI injected into Django admin changelists
8
+ - Type-aware filtering for text, numeric, boolean, choice, date, datetime and relation fields
9
+ - Field path traversal such as `author__email`
10
+ - Saved filters per authenticated user
11
+ - Column selection and ordering persisted with saved filters
12
+ - AJAX-backed relation picker using Django admin Select2 assets
13
+ - Support for custom filter fields via `SuperFilterField`
14
+ - Single JSON query parameter for rules (`sf`) and one for visible columns (`sfc`)
15
+ - Works without templates overrides
16
+
17
+ ## Requirements
18
+
19
+ - Python 3.10+
20
+ - Django 4.0+
21
+
22
+ ## Installation
23
+
24
+ Install the package:
25
+
26
+ ```bash
27
+ pip install django-admin-superfilter
28
+ ```
29
+
30
+ Add the app to `INSTALLED_APPS`:
31
+
32
+ ```python
33
+ INSTALLED_APPS = [
34
+ # ...
35
+ "superfilter",
36
+ ]
37
+ ```
38
+
39
+ Run migrations:
40
+
41
+ ```bash
42
+ python manage.py migrate
43
+ ```
44
+
45
+ ## Quick start
46
+
47
+ ```python
48
+ from django.contrib import admin
49
+ from superfilter.admin import SuperFilterAdminMixin
50
+
51
+ from .models import Bird
52
+
53
+
54
+ @admin.register(Bird)
55
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
56
+ list_display = ("species", "location", "count")
57
+ search_fields = ("species",)
58
+ ```
59
+
60
+ Important:
61
+
62
+ - Put `SuperFilterAdminMixin` before `admin.ModelAdmin` in the MRO.
63
+ - By default, filterable fields come from `list_display`.
64
+ - Non-model entries in `list_display` are ignored.
65
+ - Traversed model fields like `location__city__name` are supported.
66
+
67
+ ## What appears in the UI
68
+
69
+ The package adds a search-bar-like control above the changelist with:
70
+
71
+ - an add-filter button
72
+ - filter badges for current rules
73
+ - an Apply button
74
+ - a split menu with Save
75
+ - a Reset button
76
+ - a Columns toggle button
77
+ - a collapsible column chooser
78
+ - saved filter chips
79
+
80
+ Saved filters store both:
81
+
82
+ - the active filter rules
83
+ - the selected column list and order
84
+
85
+ ## Supported field kinds and operators
86
+
87
+ ### All fields
88
+
89
+ - `set`
90
+ - `not_set`
91
+
92
+ ### Text-like fields
93
+
94
+ Text-like fields include `CharField`, `TextField`, `EmailField`, `SlugField`, `URLField`, `UUIDField`.
95
+
96
+ Operators:
97
+
98
+ - `set`
99
+ - `not_set`
100
+ - `eq`
101
+ - `neq`
102
+ - `contains`
103
+ - `not_contains`
104
+ - `in`
105
+ - `not_in`
106
+
107
+ ### Numeric fields
108
+
109
+ Operators:
110
+
111
+ - `set`
112
+ - `not_set`
113
+ - `eq`
114
+ - `neq`
115
+ - `gt`
116
+ - `lt`
117
+ - `gte`
118
+ - `lte`
119
+
120
+ ### Boolean fields
121
+
122
+ Operators:
123
+
124
+ - `set`
125
+ - `not_set`
126
+ - `true`
127
+ - `false`
128
+
129
+ ### Choice fields
130
+
131
+ Operators:
132
+
133
+ - `set`
134
+ - `not_set`
135
+ - `in`
136
+ - `not_in`
137
+
138
+ ### Foreign key / relation fields
139
+
140
+ Operators:
141
+
142
+ - `set`
143
+ - `not_set`
144
+ - `in`
145
+ - `not_in`
146
+
147
+ ### Date and datetime fields
148
+
149
+ Operators:
150
+
151
+ - `set`
152
+ - `not_set`
153
+ - `eq`
154
+ - `before`
155
+ - `after`
156
+ - `between`
157
+
158
+ ## Semantics
159
+
160
+ - `set` means the field is considered populated
161
+ - `not_set` means the field is empty
162
+ - For text fields, empty means `NULL` or empty string
163
+ - For boolean fields, `set` / `not_set` only target `NULL` vs non-`NULL`
164
+ - `contains` uses `icontains`
165
+ - `not_contains` negates `icontains`
166
+ - `between` on date/datetime expects exactly two values
167
+ - Relation filters use selected related object primary keys
168
+
169
+ ## Configuration
170
+
171
+ `SuperFilterAdminMixin` exposes a few attributes:
172
+
173
+ - `superfilter_param_name = "sf"`
174
+ - `superfilter_columns_param_name = "sfc"`
175
+ - `superfilter_fields = None`
176
+ - `superfilter_page_size = 25`
177
+ - `superfilter_all_limit = 2000`
178
+
179
+ Example:
180
+
181
+ ```python
182
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
183
+ list_display = ("species", "location", "count")
184
+ superfilter_page_size = 50
185
+ superfilter_all_limit = 5000
186
+ ```
187
+
188
+ ## Restricting filterable fields
189
+
190
+ By default, filterable fields are taken from `list_display`.
191
+
192
+ To expose a different set of fields, use `superfilter_fields`:
193
+
194
+ ```python
195
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
196
+ list_display = ("species", "location", "count")
197
+ superfilter_fields = ("species", "count", "location__city")
198
+ ```
199
+
200
+ This only affects filterable fields. Column selection still uses `list_display`.
201
+
202
+ ## Custom filter fields
203
+
204
+ You can plug in custom fields that do not map directly to a Django model field.
205
+
206
+ Create a subclass of `SuperFilterField`:
207
+
208
+ ```python
209
+ from django.db.models import Q
210
+ from superfilter.logic import SuperFilterField
211
+
212
+
213
+ class HasLargeCountField(SuperFilterField):
214
+ path = "has_large_count"
215
+ label = "Large count"
216
+ kind = "choice"
217
+ choices = [
218
+ {"value": "yes", "label": "Yes"},
219
+ {"value": "no", "label": "No"},
220
+ ]
221
+
222
+ def apply_rule(self, queryset, rule):
223
+ values = set(rule.get("value") or [])
224
+ if "yes" in values and "no" not in values:
225
+ return queryset.filter(count__gte=100)
226
+ if "no" in values and "yes" not in values:
227
+ return queryset.filter(count__lt=100)
228
+ return queryset
229
+ ```
230
+
231
+ Register it in `superfilter_fields`:
232
+
233
+ ```python
234
+ class BirdAdmin(SuperFilterAdminMixin, admin.ModelAdmin):
235
+ list_display = ("species", "location", "count")
236
+ superfilter_fields = ("species", HasLargeCountField)
237
+ ```
238
+
239
+ ## Saved filters
240
+
241
+ Saved filters are stored in the `SavedSuperFilter` model and are scoped by:
242
+
243
+ - user
244
+ - app label
245
+ - model name
246
+ - saved filter name
247
+
248
+ Notes:
249
+
250
+ - saving requires an authenticated user
251
+ - saved filters are private to the user
252
+ - the latest saved filter with column settings is used as fallback when `sfc` is absent from the querystring
253
+
254
+ ## URL format
255
+
256
+ Rules are sent in the `sf` query parameter as JSON:
257
+
258
+ ```json
259
+ [
260
+ {"field": "species", "op": "contains", "value": "owl"},
261
+ {"field": "location", "op": "in", "value": [1, 2]},
262
+ {"field": "count", "op": "gte", "value": 10}
263
+ ]
264
+ ```
265
+
266
+ Columns are sent in the `sfc` query parameter as JSON:
267
+
268
+ ```json
269
+ ["location", "species", "count"]
270
+ ```
271
+
272
+ ## Relation option loading
273
+
274
+ For relation filters, the package exposes an admin endpoint that:
275
+
276
+ - reuses the related admin's `get_search_results()` when available
277
+ - otherwise falls back to searching up to three text fields
278
+ - otherwise falls back to PK search for numeric terms
279
+
280
+ ## Static assets
281
+
282
+ The mixin injects:
283
+
284
+ - `admin/css/vendor/select2/select2.min.css`
285
+ - `admin/js/vendor/select2/select2.full.min.js`
286
+ - `superfilter/superfilter.css`
287
+ - `superfilter/superfilter.js`
288
+
289
+ No custom template override is required.
290
+
291
+ ## Limitations
292
+
293
+ - Filtering ignores callable/computed `list_display` entries unless implemented as `SuperFilterField`
294
+ - Column selection only works on entries present in `list_display`
295
+ - Saved filters depend on migrations for the `superfilter` app being applied
296
+ - The package ships with admin-focused frontend assets and is not intended for non-admin pages
297
+
298
+ ## Example project
299
+
300
+ A runnable sample project is available in:
301
+
302
+ - `examples/sampleapp/`
303
+
304
+ Run it with:
305
+
306
+ ```bash
307
+ cd examples/sampleapp
308
+ python manage.py migrate
309
+ python manage.py runserver
310
+ ```
311
+
312
+ ## Development
313
+
314
+ Run tests from the repository root:
315
+
316
+ ```bash
317
+ python manage.py test superfilter
318
+ ```
319
+
320
+ Or use the sample project:
321
+
322
+ ```bash
323
+ cd examples/sampleapp
324
+ python manage.py test
325
+ ```
326
+
327
+ ## License
328
+
329
+ MIT. See `LICENCE.md`.