django-ninja-aio-crud 2.9.0__py3-none-any.whl → 2.10.1__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.9.0.dist-info → django_ninja_aio_crud-2.10.1.dist-info}/METADATA +1 -1
- {django_ninja_aio_crud-2.9.0.dist-info → django_ninja_aio_crud-2.10.1.dist-info}/RECORD +10 -10
- ninja_aio/__init__.py +1 -1
- ninja_aio/models/utils.py +2 -2
- ninja_aio/schemas/__init__.py +2 -0
- ninja_aio/schemas/api.py +20 -1
- ninja_aio/views/api.py +3 -0
- ninja_aio/views/mixins.py +75 -2
- {django_ninja_aio_crud-2.9.0.dist-info → django_ninja_aio_crud-2.10.1.dist-info}/WHEEL +0 -0
- {django_ninja_aio_crud-2.9.0.dist-info → django_ninja_aio_crud-2.10.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ninja_aio/__init__.py,sha256=
|
|
1
|
+
ninja_aio/__init__.py,sha256=89U1Dj8vplVSbgWDcQsklzcAzW070HQlarC0u9_LOX4,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
|
|
@@ -15,15 +15,15 @@ ninja_aio/helpers/api.py,sha256=2beyexep-ehgaA_1bV5Yuh3zRDVcRCMkrW94nmfDWEA,2081
|
|
|
15
15
|
ninja_aio/helpers/query.py,sha256=NMGkS_v-ZVYKNtf1XohEUzfwca52Eq5FTcQ5lehHjus,4682
|
|
16
16
|
ninja_aio/models/__init__.py,sha256=L3UQnQAlKoI3F7jinadL-Nn55hkPvnSRPYW0JtnbWFo,114
|
|
17
17
|
ninja_aio/models/serializers.py,sha256=I7pUz_vl0FElaVsrGaogT_Lj9T4uaNG7UDMGf5VMwW4,38468
|
|
18
|
-
ninja_aio/models/utils.py,sha256=
|
|
19
|
-
ninja_aio/schemas/__init__.py,sha256=
|
|
20
|
-
ninja_aio/schemas/api.py,sha256
|
|
18
|
+
ninja_aio/models/utils.py,sha256=dxzwqE25b0lFZ--30XxeIJB-y4xbIDtc624vGaa_wOg,32885
|
|
19
|
+
ninja_aio/schemas/__init__.py,sha256=_a092xZezlLc9QWCPWrybeklByCs39jbvf33zwdnrys,603
|
|
20
|
+
ninja_aio/schemas/api.py,sha256=InzZgIFU4Zxkgj9u_zZzAMYs_vdaPD5eu12gG7xAFoQ,1047
|
|
21
21
|
ninja_aio/schemas/generics.py,sha256=frjJsKJMAdM_NdNKv-9ddZNGxYy5PNzjIRGtuycgr-w,112
|
|
22
22
|
ninja_aio/schemas/helpers.py,sha256=Vti5BfHWpxaJXj_ixZBJb34VRwhHODrlVjRlIuHh_ug,8428
|
|
23
23
|
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.
|
|
24
|
+
ninja_aio/views/api.py,sha256=LUJBvD7oxE3nq6DlMDOHYzNi2KBYQMSbb7SzfZdqcp0,22197
|
|
25
|
+
ninja_aio/views/mixins.py,sha256=YKdJVCjya-VB8bs62DNQZ9Gj3qHpsEJ-2257RbI0K1A,14238
|
|
26
|
+
django_ninja_aio_crud-2.10.1.dist-info/licenses/LICENSE,sha256=yrDAYcm0gRp_Qyzo3GQa4BjYjWRkAhGC8QRva__RYq0,1073
|
|
27
|
+
django_ninja_aio_crud-2.10.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
|
|
28
|
+
django_ninja_aio_crud-2.10.1.dist-info/METADATA,sha256=G-t2gFVZicsgYER6sY8HKnSwcNUfBwgClgRqkMRVCUE,9964
|
|
29
|
+
django_ninja_aio_crud-2.10.1.dist-info/RECORD,,
|
ninja_aio/__init__.py
CHANGED
ninja_aio/models/utils.py
CHANGED
|
@@ -566,10 +566,10 @@ class ModelUtil:
|
|
|
566
566
|
serializable_fields = self._get_serializable_field_names(is_for)
|
|
567
567
|
for f in serializable_fields:
|
|
568
568
|
field_obj = getattr(self.model, f)
|
|
569
|
-
if isinstance(field_obj,
|
|
569
|
+
if isinstance(field_obj, ForwardOneToOneDescriptor):
|
|
570
570
|
select_rels.append(f)
|
|
571
571
|
continue
|
|
572
|
-
if isinstance(field_obj,
|
|
572
|
+
if isinstance(field_obj, ForwardManyToOneDescriptor):
|
|
573
573
|
select_rels.append(f)
|
|
574
574
|
return select_rels
|
|
575
575
|
|
ninja_aio/schemas/__init__.py
CHANGED
|
@@ -5,6 +5,7 @@ from .api import (
|
|
|
5
5
|
M2MAddSchemaIn,
|
|
6
6
|
M2MRemoveSchemaIn,
|
|
7
7
|
M2MSchemaIn,
|
|
8
|
+
RelationFilterSchema,
|
|
8
9
|
)
|
|
9
10
|
from .helpers import M2MRelationSchema, QuerySchema, ModelQuerySetSchema, ObjectQuerySchema, ObjectsQuerySchema
|
|
10
11
|
|
|
@@ -20,4 +21,5 @@ __all__ = [
|
|
|
20
21
|
"ModelQuerySetSchema",
|
|
21
22
|
"ObjectQuerySchema",
|
|
22
23
|
"ObjectsQuerySchema",
|
|
24
|
+
"RelationFilterSchema",
|
|
23
25
|
]
|
ninja_aio/schemas/api.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
1
3
|
from ninja import Schema
|
|
2
4
|
|
|
3
5
|
|
|
@@ -21,4 +23,21 @@ class M2MRemoveSchemaIn(Schema):
|
|
|
21
23
|
|
|
22
24
|
class M2MSchemaIn(Schema):
|
|
23
25
|
add: list = []
|
|
24
|
-
remove: list = []
|
|
26
|
+
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
|
ninja_aio/views/api.py
CHANGED
|
@@ -311,6 +311,9 @@ 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
|
+
|
|
314
317
|
def _auth_view(self, view_type: str):
|
|
315
318
|
"""
|
|
316
319
|
Resolve auth for a specific HTTP verb; falls back to self.auth if NOT_SET.
|
ninja_aio/views/mixins.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from ninja_aio.views.api import APIViewSet
|
|
2
|
+
from ninja_aio.schemas import RelationFilterSchema
|
|
2
3
|
|
|
3
4
|
|
|
4
5
|
class IcontainsFilterViewSetMixin(APIViewSet):
|
|
@@ -55,7 +56,7 @@ class IcontainsFilterViewSetMixin(APIViewSet):
|
|
|
55
56
|
**{
|
|
56
57
|
f"{key}__icontains": value
|
|
57
58
|
for key, value in filters.items()
|
|
58
|
-
if isinstance(value, str)
|
|
59
|
+
if isinstance(value, str) and not self._check_relations_filters(key)
|
|
59
60
|
}
|
|
60
61
|
)
|
|
61
62
|
|
|
@@ -94,7 +95,11 @@ class BooleanFilterViewSetMixin(APIViewSet):
|
|
|
94
95
|
"""
|
|
95
96
|
base_qs = await super().query_params_handler(queryset, filters)
|
|
96
97
|
return base_qs.filter(
|
|
97
|
-
**{
|
|
98
|
+
**{
|
|
99
|
+
key: value
|
|
100
|
+
for key, value in filters.items()
|
|
101
|
+
if isinstance(value, bool) and not self._check_relations_filters(key)
|
|
102
|
+
}
|
|
98
103
|
)
|
|
99
104
|
|
|
100
105
|
|
|
@@ -136,6 +141,7 @@ class NumericFilterViewSetMixin(APIViewSet):
|
|
|
136
141
|
key: value
|
|
137
142
|
for key, value in filters.items()
|
|
138
143
|
if isinstance(value, (int, float))
|
|
144
|
+
and not self._check_relations_filters(key)
|
|
139
145
|
}
|
|
140
146
|
)
|
|
141
147
|
|
|
@@ -178,6 +184,7 @@ class DateFilterViewSetMixin(APIViewSet):
|
|
|
178
184
|
f"{key}{self._compare_attr}": value
|
|
179
185
|
for key, value in filters.items()
|
|
180
186
|
if hasattr(value, "isoformat")
|
|
187
|
+
and not self._check_relations_filters(key)
|
|
181
188
|
}
|
|
182
189
|
)
|
|
183
190
|
|
|
@@ -273,3 +280,69 @@ class LessEqualDateFilterViewSetMixin(DateFilterViewSetMixin):
|
|
|
273
280
|
"""
|
|
274
281
|
|
|
275
282
|
_compare_attr = "__lte"
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
class RelationFilterViewSetMixin(APIViewSet):
|
|
286
|
+
"""
|
|
287
|
+
Mixin providing filtering for related fields in Django QuerySets.
|
|
288
|
+
|
|
289
|
+
This mixin applies filters on related fields based on configured RelationFilterSchema
|
|
290
|
+
entries. Each entry maps a query parameter name to a Django ORM lookup path.
|
|
291
|
+
|
|
292
|
+
Attributes:
|
|
293
|
+
relations_filters: List of RelationFilterSchema defining the relation filters.
|
|
294
|
+
Each schema specifies:
|
|
295
|
+
- query_param: The API query parameter name (e.g., "author_id")
|
|
296
|
+
- query_filter: The Django ORM lookup (e.g., "author__id")
|
|
297
|
+
- filter_type: Tuple of (type, default) for schema generation
|
|
298
|
+
|
|
299
|
+
Example:
|
|
300
|
+
class BookViewSet(RelationFilterViewSetMixin, APIViewSet):
|
|
301
|
+
relations_filters = [
|
|
302
|
+
RelationFilterSchema(
|
|
303
|
+
query_param="author_id",
|
|
304
|
+
query_filter="author__id",
|
|
305
|
+
filter_type=(int, None),
|
|
306
|
+
),
|
|
307
|
+
RelationFilterSchema(
|
|
308
|
+
query_param="category_slug",
|
|
309
|
+
query_filter="category__slug",
|
|
310
|
+
filter_type=(str, None),
|
|
311
|
+
),
|
|
312
|
+
]
|
|
313
|
+
|
|
314
|
+
# GET /books?author_id=5 -> queryset.filter(author__id=5)
|
|
315
|
+
# GET /books?category_slug=fiction -> queryset.filter(category__slug="fiction")
|
|
316
|
+
|
|
317
|
+
Notes:
|
|
318
|
+
- Filter values that are None or falsy are skipped.
|
|
319
|
+
- This mixin automatically registers query_params from relations_filters.
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
relations_filters: list[RelationFilterSchema] = []
|
|
323
|
+
|
|
324
|
+
def __init_subclass__(cls, **kwargs):
|
|
325
|
+
super().__init_subclass__(**kwargs)
|
|
326
|
+
cls.query_params = {
|
|
327
|
+
**cls.query_params,
|
|
328
|
+
**{
|
|
329
|
+
rel_filter.query_param: rel_filter.filter_type
|
|
330
|
+
for rel_filter in cls.relations_filters
|
|
331
|
+
},
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def relations_filters_fields(self):
|
|
336
|
+
return [rel_filter.query_param for rel_filter in self.relations_filters]
|
|
337
|
+
|
|
338
|
+
async def query_params_handler(self, queryset, filters):
|
|
339
|
+
"""
|
|
340
|
+
Apply relation filters to the queryset based on configured relations_filters.
|
|
341
|
+
"""
|
|
342
|
+
base_qs = await super().query_params_handler(queryset, filters)
|
|
343
|
+
rel_filters = {}
|
|
344
|
+
for rel_filter in self.relations_filters:
|
|
345
|
+
value = filters.get(rel_filter.query_param)
|
|
346
|
+
if value is not None:
|
|
347
|
+
rel_filters[rel_filter.query_filter] = value
|
|
348
|
+
return base_qs.filter(**rel_filters) if rel_filters else base_qs
|
|
File without changes
|
{django_ninja_aio_crud-2.9.0.dist-info → django_ninja_aio_crud-2.10.1.dist-info}/licenses/LICENSE
RENAMED
|
File without changes
|