django-ninja-aio-crud 2.10.0__py3-none-any.whl → 2.11.0__py3-none-any.whl
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.
Potentially problematic release.
This version of django-ninja-aio-crud might be problematic. Click here for more details.
- {django_ninja_aio_crud-2.10.0.dist-info → django_ninja_aio_crud-2.11.0.dist-info}/METADATA +1 -1
- {django_ninja_aio_crud-2.10.0.dist-info → django_ninja_aio_crud-2.11.0.dist-info}/RECORD +10 -9
- ninja_aio/__init__.py +1 -1
- ninja_aio/schemas/__init__.py +15 -1
- ninja_aio/schemas/api.py +0 -19
- ninja_aio/schemas/filters.py +73 -0
- ninja_aio/views/api.py +11 -0
- ninja_aio/views/mixins.py +81 -6
- {django_ninja_aio_crud-2.10.0.dist-info → django_ninja_aio_crud-2.11.0.dist-info}/WHEEL +0 -0
- {django_ninja_aio_crud-2.10.0.dist-info → django_ninja_aio_crud-2.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ninja_aio/__init__.py,sha256=
|
|
1
|
+
ninja_aio/__init__.py,sha256=HbzxvEfM0l-SDrJKZVbQVJlyH0xXwCQMNB6zSwH9Onw,120
|
|
2
2
|
ninja_aio/api.py,sha256=tuC7vdvn7s1GkCnSFy9Kn1zv0glZfYptRQVvo8ZRtGQ,2429
|
|
3
3
|
ninja_aio/auth.py,sha256=4sWdFPjKiQgUL1d_CSGDblVjnY5ptP6LQha6XXdluJA,9157
|
|
4
4
|
ninja_aio/exceptions.py,sha256=_3xFqfFCOfrrMhSA0xbMqgXy8R0UQjhXaExrFvaDAjY,3891
|
|
@@ -16,14 +16,15 @@ ninja_aio/helpers/query.py,sha256=NMGkS_v-ZVYKNtf1XohEUzfwca52Eq5FTcQ5lehHjus,46
|
|
|
16
16
|
ninja_aio/models/__init__.py,sha256=L3UQnQAlKoI3F7jinadL-Nn55hkPvnSRPYW0JtnbWFo,114
|
|
17
17
|
ninja_aio/models/serializers.py,sha256=I7pUz_vl0FElaVsrGaogT_Lj9T4uaNG7UDMGf5VMwW4,38468
|
|
18
18
|
ninja_aio/models/utils.py,sha256=dxzwqE25b0lFZ--30XxeIJB-y4xbIDtc624vGaa_wOg,32885
|
|
19
|
-
ninja_aio/schemas/__init__.py,sha256=
|
|
20
|
-
ninja_aio/schemas/api.py,sha256=
|
|
19
|
+
ninja_aio/schemas/__init__.py,sha256=dHILiYBKMb51lDcyQdiXRw_0nzqM7Lu81UX2hv7kEfo,837
|
|
20
|
+
ninja_aio/schemas/api.py,sha256=dGUpJXR1iAf93QNR4kYj1uqIkTjiMfXultCotY6GtaQ,361
|
|
21
|
+
ninja_aio/schemas/filters.py,sha256=VxzH2xSWok8cUSkyfeqtrGhRewtFVmNHQfHNvY8Aynw,2662
|
|
21
22
|
ninja_aio/schemas/generics.py,sha256=frjJsKJMAdM_NdNKv-9ddZNGxYy5PNzjIRGtuycgr-w,112
|
|
22
23
|
ninja_aio/schemas/helpers.py,sha256=Vti5BfHWpxaJXj_ixZBJb34VRwhHODrlVjRlIuHh_ug,8428
|
|
23
24
|
ninja_aio/views/__init__.py,sha256=DEzjWA6y3WF0V10nNF8eEurLNEodgxKzyFd09AqVp3s,148
|
|
24
|
-
ninja_aio/views/api.py,sha256=
|
|
25
|
-
ninja_aio/views/mixins.py,sha256=
|
|
26
|
-
django_ninja_aio_crud-2.
|
|
27
|
-
django_ninja_aio_crud-2.
|
|
28
|
-
django_ninja_aio_crud-2.
|
|
29
|
-
django_ninja_aio_crud-2.
|
|
25
|
+
ninja_aio/views/api.py,sha256=AAqkj0xT8J3PmJvsbluZ33cfrmrXJHiV9ARe2BqnfQ8,22492
|
|
26
|
+
ninja_aio/views/mixins.py,sha256=Zl6J8gbVagwT85bzDuKyJTk3iFxxFgX0YgYkjiUxZGg,17040
|
|
27
|
+
django_ninja_aio_crud-2.11.0.dist-info/licenses/LICENSE,sha256=yrDAYcm0gRp_Qyzo3GQa4BjYjWRkAhGC8QRva__RYq0,1073
|
|
28
|
+
django_ninja_aio_crud-2.11.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
29
|
+
django_ninja_aio_crud-2.11.0.dist-info/METADATA,sha256=GtNHDrOvlRcojF2TWz5UPOla1cuslKzAqA7uTqUb9Z0,9964
|
|
30
|
+
django_ninja_aio_crud-2.11.0.dist-info/RECORD,,
|
ninja_aio/__init__.py
CHANGED
ninja_aio/schemas/__init__.py
CHANGED
|
@@ -5,9 +5,20 @@ from .api import (
|
|
|
5
5
|
M2MAddSchemaIn,
|
|
6
6
|
M2MRemoveSchemaIn,
|
|
7
7
|
M2MSchemaIn,
|
|
8
|
+
)
|
|
9
|
+
from .filters import (
|
|
8
10
|
RelationFilterSchema,
|
|
11
|
+
MatchCaseFilterSchema,
|
|
12
|
+
MatchConditionFilterSchema,
|
|
13
|
+
BooleanMatchFilterSchema,
|
|
14
|
+
)
|
|
15
|
+
from .helpers import (
|
|
16
|
+
M2MRelationSchema,
|
|
17
|
+
QuerySchema,
|
|
18
|
+
ModelQuerySetSchema,
|
|
19
|
+
ObjectQuerySchema,
|
|
20
|
+
ObjectsQuerySchema,
|
|
9
21
|
)
|
|
10
|
-
from .helpers import M2MRelationSchema, QuerySchema, ModelQuerySetSchema, ObjectQuerySchema, ObjectsQuerySchema
|
|
11
22
|
|
|
12
23
|
__all__ = [
|
|
13
24
|
"GenericMessageSchema",
|
|
@@ -22,4 +33,7 @@ __all__ = [
|
|
|
22
33
|
"ObjectQuerySchema",
|
|
23
34
|
"ObjectsQuerySchema",
|
|
24
35
|
"RelationFilterSchema",
|
|
36
|
+
"MatchCaseFilterSchema",
|
|
37
|
+
"MatchConditionFilterSchema",
|
|
38
|
+
"BooleanMatchFilterSchema",
|
|
25
39
|
]
|
ninja_aio/schemas/api.py
CHANGED
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import Any
|
|
2
|
-
|
|
3
1
|
from ninja import Schema
|
|
4
2
|
|
|
5
3
|
|
|
@@ -24,20 +22,3 @@ class M2MRemoveSchemaIn(Schema):
|
|
|
24
22
|
class M2MSchemaIn(Schema):
|
|
25
23
|
add: list = []
|
|
26
24
|
remove: list = []
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
class RelationFilterSchema(Schema):
|
|
30
|
-
"""
|
|
31
|
-
Schema for configuring relation-based filters in RelationFilterViewSetMixin.
|
|
32
|
-
|
|
33
|
-
Attributes:
|
|
34
|
-
filter_type: A tuple of (type, default_value) used to generate the query parameter
|
|
35
|
-
field in the filters schema. Example: (int, None) for optional integer filter.
|
|
36
|
-
query_param: The name of the query parameter exposed in the API endpoint.
|
|
37
|
-
This is what clients will use in requests (e.g., ?author_id=5).
|
|
38
|
-
query_filter: The Django ORM lookup to apply (e.g., "author__id", "category__slug").
|
|
39
|
-
"""
|
|
40
|
-
|
|
41
|
-
filter_type: tuple[type, Any]
|
|
42
|
-
query_param: str
|
|
43
|
-
query_filter: str
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
from ninja import Schema
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class FilterSchema(Schema):
|
|
7
|
+
"""
|
|
8
|
+
Schema for configuring basic filters in FilterViewSetMixin.
|
|
9
|
+
|
|
10
|
+
Attributes:
|
|
11
|
+
filter_type: A tuple of (type, default_value) used to generate the query parameter
|
|
12
|
+
field in the filters schema. Example: (str, None) for optional string filter.
|
|
13
|
+
query_param: The name of the query parameter exposed in the API endpoint.
|
|
14
|
+
This is what clients will use in requests (e.g., ?name=example).
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
filter_type: tuple[type, Any]
|
|
18
|
+
query_param: str
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class MatchConditionFilterSchema(Schema):
|
|
22
|
+
"""
|
|
23
|
+
Schema for configuring match condition filters in MatchConditionFilterViewSetMixin.
|
|
24
|
+
Attributes:
|
|
25
|
+
query_filter: The Django ORM lookup to apply (e.g., "status", "category__name").
|
|
26
|
+
include: Whether to include records matching the condition (default: True).
|
|
27
|
+
"""
|
|
28
|
+
query_filter: dict[str, Any]
|
|
29
|
+
include: bool = True
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class BooleanMatchFilterSchema(Schema):
|
|
33
|
+
"""
|
|
34
|
+
Schema for configuring boolean match filters in BooleanMatchFilterViewSetMixin.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
true: MatchConditionFilterSchema for when the boolean filter is True.
|
|
38
|
+
false: MatchConditionFilterSchema for when the boolean filter is False.
|
|
39
|
+
"""
|
|
40
|
+
|
|
41
|
+
true: MatchConditionFilterSchema
|
|
42
|
+
false: MatchConditionFilterSchema
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class RelationFilterSchema(FilterSchema):
|
|
46
|
+
"""
|
|
47
|
+
Schema for configuring relation-based filters in RelationFilterViewSetMixin.
|
|
48
|
+
|
|
49
|
+
Attributes:
|
|
50
|
+
filter_type: A tuple of (type, default_value) used to generate the query parameter
|
|
51
|
+
field in the filters schema. Example: (int, None) for optional integer filter.
|
|
52
|
+
query_param: The name of the query parameter exposed in the API endpoint.
|
|
53
|
+
This is what clients will use in requests (e.g., ?author_id=5).
|
|
54
|
+
query_filter: The Django ORM lookup to apply (e.g., "author__id", "category__slug").
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
query_filter: str
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class MatchCaseFilterSchema(FilterSchema):
|
|
61
|
+
"""
|
|
62
|
+
Schema for configuring match-case filters in MatchCaseFilterViewSetMixin.
|
|
63
|
+
|
|
64
|
+
Attributes:
|
|
65
|
+
filter_type: A tuple of (type, default_value) used to generate the query parameter
|
|
66
|
+
field in the filters schema. Defaults to (bool, None) for optional boolean filter.
|
|
67
|
+
query_param: The name of the query parameter exposed in the API endpoint.
|
|
68
|
+
This is what clients will use in requests (e.g., ?is_active=true).
|
|
69
|
+
cases: A BooleanMatchFilterSchema defining the filter conditions for True and False cases.
|
|
70
|
+
"""
|
|
71
|
+
|
|
72
|
+
filter_type: tuple[type, Any] = (bool, None)
|
|
73
|
+
cases: BooleanMatchFilterSchema
|
ninja_aio/views/api.py
CHANGED
|
@@ -311,6 +311,17 @@ class APIViewSet(API):
|
|
|
311
311
|
"delete": (None, self.delete_view),
|
|
312
312
|
}
|
|
313
313
|
|
|
314
|
+
def _check_relations_filters(self, filter: str):
|
|
315
|
+
return filter in getattr(self, "relations_filters_fields", [])
|
|
316
|
+
|
|
317
|
+
def _check_match_cases_filters(self, filter: str):
|
|
318
|
+
return filter in getattr(self, "filters_match_cases_fields", [])
|
|
319
|
+
|
|
320
|
+
def _is_special_filter(self, filter: str):
|
|
321
|
+
return self._check_relations_filters(filter) or self._check_match_cases_filters(
|
|
322
|
+
filter
|
|
323
|
+
)
|
|
324
|
+
|
|
314
325
|
def _auth_view(self, view_type: str):
|
|
315
326
|
"""
|
|
316
327
|
Resolve auth for a specific HTTP verb; falls back to self.auth if NOT_SET.
|
ninja_aio/views/mixins.py
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
from ninja_aio.views.api import APIViewSet
|
|
2
|
-
from ninja_aio.schemas import RelationFilterSchema
|
|
2
|
+
from ninja_aio.schemas import RelationFilterSchema, MatchCaseFilterSchema
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
class IcontainsFilterViewSetMixin(APIViewSet):
|
|
@@ -56,7 +56,7 @@ class IcontainsFilterViewSetMixin(APIViewSet):
|
|
|
56
56
|
**{
|
|
57
57
|
f"{key}__icontains": value
|
|
58
58
|
for key, value in filters.items()
|
|
59
|
-
if isinstance(value, str)
|
|
59
|
+
if isinstance(value, str) and not self._is_special_filter(key)
|
|
60
60
|
}
|
|
61
61
|
)
|
|
62
62
|
|
|
@@ -95,7 +95,11 @@ class BooleanFilterViewSetMixin(APIViewSet):
|
|
|
95
95
|
"""
|
|
96
96
|
base_qs = await super().query_params_handler(queryset, filters)
|
|
97
97
|
return base_qs.filter(
|
|
98
|
-
**{
|
|
98
|
+
**{
|
|
99
|
+
key: value
|
|
100
|
+
for key, value in filters.items()
|
|
101
|
+
if isinstance(value, bool) and not self._is_special_filter(key)
|
|
102
|
+
}
|
|
99
103
|
)
|
|
100
104
|
|
|
101
105
|
|
|
@@ -136,7 +140,7 @@ class NumericFilterViewSetMixin(APIViewSet):
|
|
|
136
140
|
**{
|
|
137
141
|
key: value
|
|
138
142
|
for key, value in filters.items()
|
|
139
|
-
if isinstance(value, (int, float))
|
|
143
|
+
if isinstance(value, (int, float)) and not self._is_special_filter(key)
|
|
140
144
|
}
|
|
141
145
|
)
|
|
142
146
|
|
|
@@ -178,7 +182,7 @@ class DateFilterViewSetMixin(APIViewSet):
|
|
|
178
182
|
**{
|
|
179
183
|
f"{key}{self._compare_attr}": value
|
|
180
184
|
for key, value in filters.items()
|
|
181
|
-
if hasattr(value, "isoformat")
|
|
185
|
+
if hasattr(value, "isoformat") and not self._is_special_filter(key)
|
|
182
186
|
}
|
|
183
187
|
)
|
|
184
188
|
|
|
@@ -325,6 +329,10 @@ class RelationFilterViewSetMixin(APIViewSet):
|
|
|
325
329
|
},
|
|
326
330
|
}
|
|
327
331
|
|
|
332
|
+
@property
|
|
333
|
+
def relations_filters_fields(self):
|
|
334
|
+
return [rel_filter.query_param for rel_filter in self.relations_filters]
|
|
335
|
+
|
|
328
336
|
async def query_params_handler(self, queryset, filters):
|
|
329
337
|
"""
|
|
330
338
|
Apply relation filters to the queryset based on configured relations_filters.
|
|
@@ -335,4 +343,71 @@ class RelationFilterViewSetMixin(APIViewSet):
|
|
|
335
343
|
value = filters.get(rel_filter.query_param)
|
|
336
344
|
if value is not None:
|
|
337
345
|
rel_filters[rel_filter.query_filter] = value
|
|
338
|
-
return base_qs.filter(**rel_filters) if rel_filters else base_qs
|
|
346
|
+
return base_qs.filter(**rel_filters) if rel_filters else base_qs
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
class MatchCaseFilterViewSetMixin(APIViewSet):
|
|
350
|
+
"""
|
|
351
|
+
Mixin providing match-case filtering for Django QuerySets.
|
|
352
|
+
This mixin applies filters based on boolean query parameters defined in
|
|
353
|
+
MatchCaseFilterSchema entries. Each entry specifies different filter conditions
|
|
354
|
+
for True and False cases.
|
|
355
|
+
Attributes:
|
|
356
|
+
filters_match_cases: List of MatchCaseFilterSchema defining the match-case filters.
|
|
357
|
+
Each schema specifies:
|
|
358
|
+
- query_param: The API query parameter name (e.g., "is_active")
|
|
359
|
+
- cases: A BooleanMatchFilterSchema with 'true' and 'false' MatchConditionFilterSchema
|
|
360
|
+
Example:
|
|
361
|
+
class UserViewSet(MatchCaseFilterViewSetMixin, APIViewSet):
|
|
362
|
+
filters_match_cases = [
|
|
363
|
+
MatchCaseFilterSchema(
|
|
364
|
+
query_param="is_active",
|
|
365
|
+
cases=BooleanMatchFilterSchema(
|
|
366
|
+
true=MatchConditionFilterSchema(
|
|
367
|
+
query_filter={"status": "active"},
|
|
368
|
+
include=True,
|
|
369
|
+
),
|
|
370
|
+
false=MatchConditionFilterSchema(
|
|
371
|
+
query_filter={"status": "inactive"},
|
|
372
|
+
include=True,
|
|
373
|
+
),
|
|
374
|
+
),
|
|
375
|
+
),
|
|
376
|
+
]
|
|
377
|
+
# GET /users?is_active=true -> queryset.filter(status="active")
|
|
378
|
+
# GET /users?is_active=false -> queryset.filter(status="inactive")
|
|
379
|
+
Notes:
|
|
380
|
+
- If the query parameter is not provided, no filtering is applied for that case.
|
|
381
|
+
- This mixin automatically registers query_params from filters_match_cases.
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
filters_match_cases: list[MatchCaseFilterSchema] = []
|
|
385
|
+
|
|
386
|
+
def __init_subclass__(cls, **kwargs):
|
|
387
|
+
super().__init_subclass__(**kwargs)
|
|
388
|
+
cls.query_params = {
|
|
389
|
+
**cls.query_params,
|
|
390
|
+
**{
|
|
391
|
+
filter_match.query_param: filter_match.filter_type
|
|
392
|
+
for filter_match in cls.filters_match_cases
|
|
393
|
+
},
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
@property
|
|
397
|
+
def filters_match_cases_fields(self):
|
|
398
|
+
return [filter_match.query_param for filter_match in self.filters_match_cases]
|
|
399
|
+
|
|
400
|
+
async def query_params_handler(self, queryset, filters):
|
|
401
|
+
base_qs = await super().query_params_handler(queryset, filters)
|
|
402
|
+
for filter_match in self.filters_match_cases:
|
|
403
|
+
value = filters.get(filter_match.query_param)
|
|
404
|
+
if value is not None:
|
|
405
|
+
case_filter = (
|
|
406
|
+
filter_match.cases.true if value else filter_match.cases.false
|
|
407
|
+
)
|
|
408
|
+
lookup = case_filter.query_filter
|
|
409
|
+
if case_filter.include:
|
|
410
|
+
base_qs = base_qs.filter(**lookup)
|
|
411
|
+
else:
|
|
412
|
+
base_qs = base_qs.exclude(**lookup)
|
|
413
|
+
return base_qs
|
|
File without changes
|
{django_ninja_aio_crud-2.10.0.dist-info → django_ninja_aio_crud-2.11.0.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|