django-ninja-aio-crud 2.9.0__py3-none-any.whl → 2.10.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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-ninja-aio-crud
3
- Version: 2.9.0
3
+ Version: 2.10.0
4
4
  Summary: Django Ninja AIO CRUD - Rest Framework
5
5
  Author: Giuseppe Casillo
6
6
  Requires-Python: >=3.10, <3.15
@@ -1,4 +1,4 @@
1
- ninja_aio/__init__.py,sha256=YE75I1ewNyWylWEf1tZjFFmQlFLdsdYyYiQgxSr1Jdk,119
1
+ ninja_aio/__init__.py,sha256=FZKyvJwguGusKjYpVUXSYB45Fssox9yEFHg47WX0BaE,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=N7waJGOCsoFvbbO1xXy94acKeG85WRK3hjZJz7IukhU,32885
19
- ninja_aio/schemas/__init__.py,sha256=iLBwHg0pmL9k_UkIui5Q8QIl_gO4fgxSv2JHxDzqnSI,549
20
- ninja_aio/schemas/api.py,sha256=-VwXhBRhmMsZLIAmWJ-P7tB5klxXS75eukjabeKKYsc,360
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
24
  ninja_aio/views/api.py,sha256=CW74JB1tsdWfv3Y3x4K9o8yXsrMxCKWZ4QalhqJGan8,22072
25
- ninja_aio/views/mixins.py,sha256=Jh6BG8Cs823nurVlODlzCquTxKrLH7Pmo5udPqUGZek,11378
26
- django_ninja_aio_crud-2.9.0.dist-info/licenses/LICENSE,sha256=yrDAYcm0gRp_Qyzo3GQa4BjYjWRkAhGC8QRva__RYq0,1073
27
- django_ninja_aio_crud-2.9.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
28
- django_ninja_aio_crud-2.9.0.dist-info/METADATA,sha256=7hbmX5K1ixoBn0RnFlkpnp3runSgZ1pi70v606cUX3Q,9963
29
- django_ninja_aio_crud-2.9.0.dist-info/RECORD,,
25
+ ninja_aio/views/mixins.py,sha256=01kEGMmpD3eTd8R5mX8sNr7KoizOfN0wvbV5y180IdE,13835
26
+ django_ninja_aio_crud-2.10.0.dist-info/licenses/LICENSE,sha256=yrDAYcm0gRp_Qyzo3GQa4BjYjWRkAhGC8QRva__RYq0,1073
27
+ django_ninja_aio_crud-2.10.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
28
+ django_ninja_aio_crud-2.10.0.dist-info/METADATA,sha256=-cSLMUj-s7djNL2RxRhLNkErv0yM49NK8CA8-zp-4zw,9964
29
+ django_ninja_aio_crud-2.10.0.dist-info/RECORD,,
ninja_aio/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """Django Ninja AIO CRUD - Rest Framework"""
2
2
 
3
- __version__ = "2.9.0"
3
+ __version__ = "2.10.0"
4
4
 
5
5
  from .api import NinjaAIO
6
6
 
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, ForwardManyToOneDescriptor):
569
+ if isinstance(field_obj, ForwardOneToOneDescriptor):
570
570
  select_rels.append(f)
571
571
  continue
572
- if isinstance(field_obj, ForwardOneToOneDescriptor):
572
+ if isinstance(field_obj, ForwardManyToOneDescriptor):
573
573
  select_rels.append(f)
574
574
  return select_rels
575
575
 
@@ -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/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):
@@ -273,3 +274,65 @@ class LessEqualDateFilterViewSetMixin(DateFilterViewSetMixin):
273
274
  """
274
275
 
275
276
  _compare_attr = "__lte"
277
+
278
+
279
+ class RelationFilterViewSetMixin(APIViewSet):
280
+ """
281
+ Mixin providing filtering for related fields in Django QuerySets.
282
+
283
+ This mixin applies filters on related fields based on configured RelationFilterSchema
284
+ entries. Each entry maps a query parameter name to a Django ORM lookup path.
285
+
286
+ Attributes:
287
+ relations_filters: List of RelationFilterSchema defining the relation filters.
288
+ Each schema specifies:
289
+ - query_param: The API query parameter name (e.g., "author_id")
290
+ - query_filter: The Django ORM lookup (e.g., "author__id")
291
+ - filter_type: Tuple of (type, default) for schema generation
292
+
293
+ Example:
294
+ class BookViewSet(RelationFilterViewSetMixin, APIViewSet):
295
+ relations_filters = [
296
+ RelationFilterSchema(
297
+ query_param="author_id",
298
+ query_filter="author__id",
299
+ filter_type=(int, None),
300
+ ),
301
+ RelationFilterSchema(
302
+ query_param="category_slug",
303
+ query_filter="category__slug",
304
+ filter_type=(str, None),
305
+ ),
306
+ ]
307
+
308
+ # GET /books?author_id=5 -> queryset.filter(author__id=5)
309
+ # GET /books?category_slug=fiction -> queryset.filter(category__slug="fiction")
310
+
311
+ Notes:
312
+ - Filter values that are None or falsy are skipped.
313
+ - This mixin automatically registers query_params from relations_filters.
314
+ """
315
+
316
+ relations_filters: list[RelationFilterSchema] = []
317
+
318
+ def __init_subclass__(cls, **kwargs):
319
+ super().__init_subclass__(**kwargs)
320
+ cls.query_params = {
321
+ **cls.query_params,
322
+ **{
323
+ rel_filter.query_param: rel_filter.filter_type
324
+ for rel_filter in cls.relations_filters
325
+ },
326
+ }
327
+
328
+ async def query_params_handler(self, queryset, filters):
329
+ """
330
+ Apply relation filters to the queryset based on configured relations_filters.
331
+ """
332
+ base_qs = await super().query_params_handler(queryset, filters)
333
+ rel_filters = {}
334
+ for rel_filter in self.relations_filters:
335
+ value = filters.get(rel_filter.query_param)
336
+ if value is not None:
337
+ rel_filters[rel_filter.query_filter] = value
338
+ return base_qs.filter(**rel_filters) if rel_filters else base_qs