django-ninja-aio-crud 2.0.0__py3-none-any.whl → 2.0.0rc1__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: django-ninja-aio-crud
3
- Version: 2.0.0
3
+ Version: 2.0.0rc1
4
4
  Summary: Django Ninja AIO CRUD - Rest Framework
5
5
  Author: Giuseppe Casillo
6
6
  Requires-Python: >=3.10
@@ -17,17 +17,15 @@ Classifier: License :: OSI Approved :: MIT License
17
17
  Classifier: Programming Language :: Python :: 3.10
18
18
  Classifier: Programming Language :: Python :: 3.11
19
19
  Classifier: Programming Language :: Python :: 3.12
20
- Classifier: Programming Language :: Python :: 3.13
21
- Classifier: Programming Language :: Python :: 3.14
22
20
  Classifier: Programming Language :: Python :: 3 :: Only
23
21
  Classifier: Framework :: Django
24
22
  Classifier: Framework :: AsyncIO
25
23
  Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
26
24
  Classifier: Topic :: Internet :: WWW/HTTP
27
25
  License-File: LICENSE
28
- Requires-Dist: django-ninja >=1.3.0, <=1.5.1
26
+ Requires-Dist: django-ninja >=1.3.0, <=1.5.0
29
27
  Requires-Dist: joserfc >=1.0.0, <= 1.4.1
30
- Requires-Dist: orjson >= 3.10.7, <= 3.11.5
28
+ Requires-Dist: orjson >= 3.10.7, <= 3.11.4
31
29
  Requires-Dist: coverage ; extra == "test"
32
30
  Project-URL: Documentation, https://django-ninja-aio.com
33
31
  Project-URL: Repository, https://github.com/caspel26/django-ninja-aio-crud
@@ -1,21 +1,21 @@
1
- ninja_aio/__init__.py,sha256=cH3S5kz6llYjarJhndCM9pmxriBwh3GJdi7HBMPj5v8,119
1
+ ninja_aio/__init__.py,sha256=jrKqucbfHt32QKtv_Z_sLBKN5ByouVattkfgikaSrAo,123
2
2
  ninja_aio/api.py,sha256=SS1TYUiFkdYjfJLVy6GI90GOzvIHzPEeL-UcqWFRHkM,1684
3
3
  ninja_aio/auth.py,sha256=8jaEp7oEJvUUB9EuyE2fOYk-khyAaekT3i80E7AbgOA,5101
4
- ninja_aio/decorators.py,sha256=BHoFIiqdIVMFqSxGh-F6WeZFo1xZK4ieDw3dzKfxZIM,8147
4
+ ninja_aio/decorators.py,sha256=gswkwl1zWSpW8VxGCe8MlgXcHMg6Y7V1f2ertey9Tjo,5522
5
5
  ninja_aio/exceptions.py,sha256=1-iRbrloIyi0CR6Tcrn5YR4_LloA7PPohKIBaxXJ0-8,2596
6
6
  ninja_aio/models.py,sha256=aJlo5a64O4o-fB8QESLMUJpoA5kcjRJxPBiAIMxg46k,47652
7
7
  ninja_aio/parsers.py,sha256=e_4lGCPV7zs-HTqtdJTc8yQD2KPAn9njbL8nF_Mmgkc,153
8
- ninja_aio/renders.py,sha256=VtmSliRJyZ6gjyoib8AXMVUBYF1jPNsiceCHujI_mAs,1699
8
+ ninja_aio/renders.py,sha256=5TdSQI8e4x3Gb2tAw1AaxrbU-asVjf2chWMr8x2Tt80,1485
9
9
  ninja_aio/types.py,sha256=TJSGlA7bt4g9fvPhJ7gzH5tKbLagPmZUzfgttEOp4xs,468
10
- ninja_aio/views.py,sha256=8vMFw-8au9O0Hpf-79jvB47PwokCzm7P8UY0DDWpN5A,16786
10
+ ninja_aio/views.py,sha256=u6WVXvErF3DAOTONTH_EhSg-5qhFBhtR4L2lNrt00qo,16331
11
11
  ninja_aio/helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- ninja_aio/helpers/api.py,sha256=kgHxYPfbV6kQbp8Z4qMwQtwFiFIcRAenxI9MDowGtis,20181
12
+ ninja_aio/helpers/api.py,sha256=lkh8eA9OnFlFPpxhnVP_wMHWPKfueVB4AnsXxgzp-HI,17426
13
13
  ninja_aio/helpers/query.py,sha256=tE8RjXvSig-WB_0LRQ0LqoE4G_HMHsu0Na5QzTNIm6U,4262
14
14
  ninja_aio/schemas/__init__.py,sha256=iLBwHg0pmL9k_UkIui5Q8QIl_gO4fgxSv2JHxDzqnSI,549
15
15
  ninja_aio/schemas/api.py,sha256=-VwXhBRhmMsZLIAmWJ-P7tB5klxXS75eukjabeKKYsc,360
16
16
  ninja_aio/schemas/generics.py,sha256=frjJsKJMAdM_NdNKv-9ddZNGxYy5PNzjIRGtuycgr-w,112
17
- ninja_aio/schemas/helpers.py,sha256=rmE0D15lJg95Unv8PU44Hbf0VDTcErMCZZFG3D_znTo,2823
18
- django_ninja_aio_crud-2.0.0.dist-info/licenses/LICENSE,sha256=yrDAYcm0gRp_Qyzo3GQa4BjYjWRkAhGC8QRva__RYq0,1073
19
- django_ninja_aio_crud-2.0.0.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
20
- django_ninja_aio_crud-2.0.0.dist-info/METADATA,sha256=FKp28V4DMReIrIFlseCRVJy5hZezdESq4ZC0nw3DikA,8672
21
- django_ninja_aio_crud-2.0.0.dist-info/RECORD,,
17
+ ninja_aio/schemas/helpers.py,sha256=al1G5-CdB9CF3eIPdZcUSE7vKRJiTU5g1ltoPhI2Onw,2622
18
+ django_ninja_aio_crud-2.0.0rc1.dist-info/licenses/LICENSE,sha256=yrDAYcm0gRp_Qyzo3GQa4BjYjWRkAhGC8QRva__RYq0,1073
19
+ django_ninja_aio_crud-2.0.0rc1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
20
+ django_ninja_aio_crud-2.0.0rc1.dist-info/METADATA,sha256=3sHPpIDJh65PwwiM4fN-ZuSDPif--X8aivMrquJiCyg,8573
21
+ django_ninja_aio_crud-2.0.0rc1.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.0.0"
3
+ __version__ = "2.0.0-rc1"
4
4
 
5
5
  from .api import NinjaAIO
6
6
 
ninja_aio/decorators.py CHANGED
@@ -143,76 +143,3 @@ def unique_view(self: object | str, plural: bool = False):
143
143
  return func # Return original function (no wrapper)
144
144
 
145
145
  return decorator
146
-
147
-
148
- def decorate_view(*decorators):
149
- """
150
- Compose and apply multiple decorators to a view (sync or async) without adding an extra wrapper.
151
-
152
- This utility was introduced to support class-based patterns where Django Ninja’s
153
- built-in `decorate_view` does not fit well. For APIs implemented with vanilla
154
- Django Ninja (function-based style), you should continue using Django Ninja’s
155
- native `decorate_view`.
156
-
157
- Behavior:
158
- - Applies decorators in the same order as Python’s stacking syntax:
159
- @d1
160
- @d2
161
- is equivalent to: view = d1(d2(view))
162
- - Supports both synchronous and asynchronous views.
163
- - Ignores None values, enabling conditional decoration.
164
- - Does not introduce an additional wrapper; composition depends on each
165
- decorator for signature/metadata preservation (e.g., using functools.wraps).
166
-
167
- *decorators: Decorator callables to apply to the target view. Any None values
168
- are skipped.
169
-
170
- Callable: A decorator that applies the provided decorators in Python stacking order.
171
-
172
- Method usage in class-based patterns:
173
-
174
- Args:
175
- *decorators: Decorator callables to apply to the target view. Any None
176
- values are skipped.
177
-
178
- Returns:
179
- A decorator that applies the provided decorators in Python stacking order.
180
-
181
- Examples:
182
- Basic usage:
183
- class MyAPIViewSet(APIViewSet):
184
- api = api
185
- model = MyModel
186
-
187
- def views(self):
188
- @self.router.get('some-endpoint/')
189
- @decorate_view(authenticate, log_request)
190
- async def some_view(request):
191
- ...
192
-
193
- Conditional decoration (skips None):
194
- class MyAPIViewSet(APIViewSet):
195
- api = api
196
- model = MyModel
197
- cache_dec = cache_page(60) if settings.ENABLE_CACHE else None
198
- def views(self):
199
- @self.router.get('data/')
200
- @decorate_view(self.cache_dec, authenticate)
201
- async def data_view(request):
202
- ...
203
-
204
- Notes:
205
- - Each decorator is applied in the order provided, with the first decorator
206
- wrapping the result of the second, and so on.
207
- - Ensure that each decorator is compatible with the view’s sync/async nature.
208
- """
209
-
210
- def _decorator(view):
211
- wrapped = view
212
- for dec in reversed(decorators):
213
- if dec is None:
214
- continue
215
- wrapped = dec(wrapped)
216
- return wrapped
217
-
218
- return _decorator
ninja_aio/helpers/api.py CHANGED
@@ -38,34 +38,27 @@ class ManyToManyAPI:
38
38
  Core behaviors:
39
39
  - Dynamically generates per-relation filter schemas for query parameters.
40
40
  - Supports custom per-relation query filtering handlers on the parent view set
41
- via a `{related_name}_query_params_handler` coroutine for GET list filters.
42
- - Supports custom per-relation object resolution for add/remove validation via
43
- `{related_name}_query_handler(request, pk, instance)` used during POST to
44
- resolve a single related object before mutation.
41
+ via a `{related_name}_query_params_handler` coroutine.
45
42
  - Validates requested add/remove primary keys, producing granular success and
46
- error feedback.
43
+ error feedback.
47
44
  - Performs add/remove operations concurrently using asyncio.gather when both
48
- types of operations are requested in the same call.
45
+ types of operations are requested in the same call.
49
46
 
50
47
  Attributes established at initialization:
51
48
  relations: list of M2MRelationSchema defining each relation.
52
49
  view_set: The parent APIViewSet instance from which router, pagination, model util,
53
- and path schema are derived.
50
+ and path schema are derived.
54
51
  router: Ninja router used to register generated endpoints.
55
52
  pagination_class: Pagination class used for GET related endpoints.
56
53
  path_schema: Pydantic schema used to validate path parameters (e.g., primary key).
57
54
  related_model_util: A ModelUtil instance cloned from the parent view set to access
58
- base object retrieval helpers.
55
+ base object retrieval helpers.
59
56
  relations_filters_schemas: Mapping of related_name -> generated Pydantic filter schema.
60
57
 
61
58
  Generated endpoint naming conventions:
62
59
  GET -> get_{base_model_name}_{relation_path}
63
60
  POST -> manage_{base_model_name}_{relation_path}
64
61
 
65
- Endpoint registration details:
66
- - GET: registered at `{retrieve_path}{relation_path}` with pagination; accepts Query filters.
67
- - POST: registered at `{retrieve_path}{relation_path}/` (trailing slash) to manage add/remove.
68
-
69
62
  All responses standardize success and error reporting for POST as:
70
63
  {
71
64
  "results": {"count": int, "details": [str, ...]},
@@ -73,29 +66,25 @@ class ManyToManyAPI:
73
66
  }
74
67
 
75
68
  Concurrency note:
76
- - Add and remove operations are executed concurrently when both lists are non-empty,
77
- minimizing round-trip latency for bulk mutations.
78
- - Uses related_manager.aadd(...) and related_manager.aremove(...) inside asyncio.gather.
69
+ Add and remove operations are executed concurrently when both lists are non-empty,
70
+ minimizing round-trip latency for bulk mutations.
79
71
 
80
72
  Error semantics:
81
73
  - Missing related objects: reported individually.
82
74
  - Invalid operation context (e.g., removing objects not currently related or adding
83
- objects already related) reported per primary key.
75
+ objects already related) reported per primary key.
84
76
  - Successful operations yield a corresponding success detail string per PK.
85
77
 
86
78
  Security / auth:
87
79
  - Each relation may optionally override auth via its schema; otherwise falls back
88
- to a default configured on the instance (self.default_auth).
80
+ to a default configured on the instance (self.default_auth).
89
81
 
90
82
  Pagination:
91
83
  - Applied only to GET related endpoints via @paginate(self.pagination_class).
92
84
 
93
85
  Extensibility:
94
86
  - Provide custom query param handling by defining an async method on the parent
95
- view set: `<related_name>_query_params_handler(self, queryset, filters_dict)`.
96
- - Provide custom per-PK resolution for POST validation by defining an async method:
97
- `<related_name>_query_handler(self, request, pk, instance)` returning a queryset,
98
- from which .afirst() is used to resolve the single target object.
87
+ view set: `<related_name>_query_params_handler(self, queryset, filters_dict)`.
99
88
  - Customize relation filtering schema via each relation's `filters` definition.
100
89
 
101
90
  -----------------------------------------------------------------------
@@ -125,44 +114,28 @@ class ManyToManyAPI:
125
114
 
126
115
  -----------------------------------------------------------------------
127
116
 
128
- _get_query_params_handler(related_name)
129
- Retrieve an optional per-relation query handler from the parent view set for GET list filters.
130
- Naming convention: `<related_name}_query_params_handler`.
131
-
132
- Parameters:
133
- related_name (str): The relation's attribute name on the base model.
134
-
135
- Returns:
136
- Coroutine | None: Handler to transform or filter the queryset based on query params.
137
-
138
- -----------------------------------------------------------------------
139
-
140
117
  _get_query_handler(related_name)
141
- Retrieve an optional per-relation single-object resolution handler from the parent view set
142
- used during POST add/remove validation. If present, it receives `(request, pk, instance)` and
143
- should return a queryset from which `.afirst()` will resolve the target object.
118
+ Retrieve an optional per-relation query handler coroutine from the parent view set.
119
+ Naming convention: `<related_name>_query_params_handler`.
144
120
 
145
121
  Parameters:
146
122
  related_name (str): The relation's attribute name on the base model.
147
123
 
148
124
  Returns:
149
- Coroutine | None: Handler to resolve a single related object by pk.
125
+ Coroutine | None: Handler to transform or filter the queryset based on query params.
150
126
 
151
127
  -----------------------------------------------------------------------
152
128
 
153
- _check_m2m_objs(request, objs_pks, related_model, related_manager, related_name, instance, remove=False)
129
+ _check_m2m_objs(request, objs_pks, model, related_manager, remove=False)
154
130
  Validate requested primary keys for add/remove operations against the current
155
131
  relation state. Performs existence checks and logical consistency (e.g., prevents
156
- adding already-related objects or removing non-related objects). Uses `_get_query_handler`
157
- when available; otherwise falls back to ModelUtil(...).get_objects(...) to resolve by pk.
132
+ adding already-related objects or removing non-related objects).
158
133
 
159
134
  Parameters:
160
135
  request (HttpRequest): Incoming request context (passed to ModelUtil for access control).
161
136
  objs_pks (list): List of primary keys to add or remove.
162
- related_model (ModelSerializer | Model): Model class or serializer used to resolve objects.
137
+ model (ModelSerializer | Model): Model class or serializer used to resolve objects.
163
138
  related_manager (QuerySet): Related manager for the base object's M2M field.
164
- related_name (str): M2M field name on the base object.
165
- instance (ModelSerializer | Model): Base object instance (owner of the relation).
166
139
  remove (bool): If True, treat operation as removal validation.
167
140
 
168
141
  Returns:
@@ -177,7 +150,7 @@ class ManyToManyAPI:
177
150
 
178
151
  -----------------------------------------------------------------------
179
152
 
180
- _collect_m2m(request, pks, model, related_manager, related_name, instance, remove=False)
153
+ _collect_m2m(request, pks, model, related_manager, remove=False)
181
154
  Wrapper around _check_m2m_objs that short-circuits on empty PK lists.
182
155
 
183
156
  Parameters:
@@ -185,8 +158,6 @@ class ManyToManyAPI:
185
158
  pks (list): Primary keys proposed for mutation.
186
159
  model (ModelSerializer | Model)
187
160
  related_manager (QuerySet)
188
- related_name (str)
189
- instance (ModelSerializer | Model)
190
161
  remove (bool): Operation type flag.
191
162
 
192
163
  Returns:
@@ -194,30 +165,38 @@ class ManyToManyAPI:
194
165
 
195
166
  -----------------------------------------------------------------------
196
167
 
197
- _register_get_relation_view(...)
198
- Registers the GET endpoint for listing related objects. Applies optional
199
- query params handler for filtering. Uses pagination and serializes via `list_read_s`.
200
-
201
- -----------------------------------------------------------------------
202
-
203
- _register_manage_relation_view(...)
204
- Registers the POST endpoint for adding/removing related objects. Validates via
205
- `_collect_m2m` and executes mutations concurrently using `aadd`/`aremove` with
206
- `asyncio.gather`. Aggregates per-PK results and errors into a standardized payload.
207
-
208
- -----------------------------------------------------------------------
209
-
210
168
  _build_views(relation)
211
169
  Dynamically define and register the GET and/or POST endpoints for a single M2M
212
170
  relation based on the relation's schema flags (get/add/remove). Builds filter
213
171
  schemas, resolves path fragments, and binds handlers to the router with unique
214
172
  operation IDs.
215
173
 
174
+ Parameters:
175
+ relation (M2MRelationSchema): Declarative specification for one M2M relation.
176
+
177
+ Side effects:
178
+ - Registers endpoints on self.router.
179
+ - Creates closures (get_related / manage_related) capturing relation context.
180
+
181
+ GET endpoint behavior:
182
+ - Retrieves base object via related_model_util.
183
+ - Fetches all related objects; applies optional query handler and filters.
184
+ - Serializes each related object with rel_util.read_s.
185
+
186
+ POST endpoint behavior:
187
+ - Parses add/remove PK lists.
188
+ - Validates objects via _collect_m2m.
189
+ - Performs asynchronous add/remove operations using aadd / aremove.
190
+ - Aggregates results and errors into standardized response payload.
191
+
216
192
  -----------------------------------------------------------------------
217
193
 
218
194
  _add_views()
219
195
  Iterates over all declared relations and invokes _build_views to attach endpoints.
220
196
 
197
+ Side effects:
198
+ - Populates router with all required M2M endpoints.
199
+
221
200
  -----------------------------------------------------------------------
222
201
 
223
202
  Usage Example (conceptual):
@@ -262,56 +241,41 @@ class ManyToManyAPI:
262
241
  for data in self.relations
263
242
  }
264
243
 
265
- def _get_query_params_handler(self, related_name: str) -> Coroutine | None:
266
- return getattr(self.view_set, f"{related_name}_query_params_handler", None)
267
-
268
244
  def _get_query_handler(self, related_name: str) -> Coroutine | None:
269
- return getattr(self.view_set, f"{related_name}_query_handler", None)
245
+ return getattr(self.view_set, f"{related_name}_query_params_handler", None)
270
246
 
271
247
  async def _check_m2m_objs(
272
248
  self,
273
249
  request: HttpRequest,
274
250
  objs_pks: list,
275
- related_model: ModelSerializer | Model,
251
+ model: ModelSerializer | Model,
276
252
  related_manager: QuerySet,
277
- related_name: str,
278
- instance: ModelSerializer | Model,
279
253
  remove: bool = False,
280
254
  ):
281
255
  """
282
256
  Validate requested add/remove pk list for M2M operations.
283
257
  Returns (errors, details, objects_to_process).
284
- Uses per-PK query handler if available, else falls back to ModelUtil lookup by pk.
285
258
  """
286
259
  errors, objs_detail, objs = [], [], []
287
260
  rel_objs = [rel_obj async for rel_obj in related_manager.select_related().all()]
288
- rel_model_name = related_model._meta.verbose_name.capitalize()
261
+ rel_model_name = model._meta.verbose_name.capitalize()
289
262
  for obj_pk in objs_pks:
290
- if query_handler := self._get_query_handler(related_name):
291
- rel_obj = await (
292
- await query_handler(
293
- request,
294
- obj_pk,
295
- instance,
296
- )
297
- ).afirst()
298
- else:
299
- rel_obj = await (
300
- await ModelUtil(related_model).get_objects(
301
- request, query_data=ObjectsQuerySchema(filters={"pk": obj_pk})
302
- )
303
- ).afirst()
263
+ rel_obj = await (
264
+ await ModelUtil(model).get_objects(
265
+ request, query_data=ObjectsQuerySchema(filters={"pk": obj_pk})
266
+ )
267
+ ).afirst()
304
268
  if rel_obj is None:
305
269
  errors.append(f"{rel_model_name} with pk {obj_pk} not found.")
306
270
  continue
307
271
  if remove ^ (rel_obj in rel_objs):
308
272
  errors.append(
309
- f"{rel_model_name} with pk {obj_pk} is {'not ' if remove else ''}in {self.related_model_util.model_name}"
273
+ f"{rel_model_name} with id {obj_pk} is {'not ' if remove else ''}in {self.related_model_util.model_name}"
310
274
  )
311
275
  continue
312
276
  objs.append(rel_obj)
313
277
  objs_detail.append(
314
- f"{rel_model_name} with pk {obj_pk} successfully {'removed' if remove else 'added'}"
278
+ f"{rel_model_name} with id {obj_pk} successfully {'removed' if remove else 'added'}"
315
279
  )
316
280
  return errors, objs_detail, objs
317
281
 
@@ -319,22 +283,14 @@ class ManyToManyAPI:
319
283
  self,
320
284
  request: HttpRequest,
321
285
  pks: list,
322
- reletad_model: ModelSerializer | Model,
286
+ model: ModelSerializer | Model,
323
287
  related_manager: QuerySet,
324
- related_name: str,
325
- instance: ModelSerializer | Model,
326
- remove: bool = False,
288
+ remove=False,
327
289
  ):
328
290
  if not pks:
329
291
  return ([], [], [])
330
292
  return await self._check_m2m_objs(
331
- request,
332
- pks,
333
- reletad_model,
334
- related_manager,
335
- related_name,
336
- instance,
337
- remove=remove,
293
+ request, pks, model, related_manager, remove=remove
338
294
  )
339
295
 
340
296
  def _register_get_relation_view(
@@ -370,14 +326,16 @@ class ManyToManyAPI:
370
326
  related_manager = getattr(obj, related_name)
371
327
  related_qs = related_manager.all()
372
328
 
373
- query_handler = self._get_query_params_handler(related_name)
329
+ query_handler = self._get_query_handler(related_name)
374
330
  if filters is not None and query_handler:
375
331
  if asyncio.iscoroutinefunction(query_handler):
376
332
  related_qs = await query_handler(related_qs, filters.model_dump())
377
333
  else:
378
334
  related_qs = query_handler(related_qs, filters.model_dump())
379
335
 
380
- return await rel_util.list_read_s(related_schema, request, related_qs)
336
+ return await rel_util.list_read_s(
337
+ related_schema, request, related_qs
338
+ )
381
339
 
382
340
  def _resolve_action_schema(self, add: bool, remove: bool):
383
341
  return self.views_action_map[(add, remove)]
@@ -385,7 +343,7 @@ class ManyToManyAPI:
385
343
  def _register_manage_relation_view(
386
344
  self,
387
345
  *,
388
- related_model: ModelSerializer | Model,
346
+ model,
389
347
  related_name: str,
390
348
  m2m_auth,
391
349
  rel_util: ModelUtil,
@@ -422,21 +380,10 @@ class ManyToManyAPI:
422
380
  remove_pks = getattr(data, "remove", []) if m2m_remove else []
423
381
 
424
382
  add_errors, add_details, add_objs = await self._collect_m2m(
425
- request,
426
- add_pks,
427
- related_model,
428
- related_manager,
429
- related_name,
430
- obj,
383
+ request, add_pks, model, related_manager
431
384
  )
432
385
  remove_errors, remove_details, remove_objs = await self._collect_m2m(
433
- request,
434
- remove_pks,
435
- related_model,
436
- related_manager,
437
- related_name,
438
- obj,
439
- remove=True,
386
+ request, remove_pks, model, related_manager, remove=True
440
387
  )
441
388
 
442
389
  tasks = []
@@ -476,7 +423,7 @@ class ManyToManyAPI:
476
423
 
477
424
  if m2m_add or m2m_remove:
478
425
  self._register_manage_relation_view(
479
- related_model=model,
426
+ model=model,
480
427
  related_name=related_name,
481
428
  m2m_auth=m2m_auth,
482
429
  rel_util=rel_util,
ninja_aio/renders.py CHANGED
@@ -4,13 +4,11 @@ from typing import Any
4
4
 
5
5
  import orjson
6
6
  from django.http import HttpRequest
7
- from django.conf import settings
8
7
  from ninja.renderers import BaseRenderer
9
8
 
10
9
 
11
10
  class ORJSONRenderer(BaseRenderer):
12
11
  media_type = "application/json"
13
- option = getattr(settings, "NINJA_AIO_ORJSON_RENDERER_OPTION", None)
14
12
 
15
13
  def render(self, request: HttpRequest, data: dict, *, response_status):
16
14
  try:
@@ -18,13 +16,9 @@ class ORJSONRenderer(BaseRenderer):
18
16
  for k, v in old_d.items():
19
17
  if isinstance(v, list):
20
18
  data |= {k: self.render_list(v)}
21
- return self.dumps(self.render_dict(data))
19
+ return orjson.dumps(self.render_dict(data))
22
20
  except AttributeError:
23
- return self.dumps(data)
24
-
25
- @classmethod
26
- def dumps(cls, data: dict) -> bytes:
27
- return orjson.dumps(data, option=cls.option)
21
+ return orjson.dumps(data)
28
22
 
29
23
  @classmethod
30
24
  def render_list(cls, data: list[dict]) -> list[dict]:
@@ -1,4 +1,4 @@
1
- from typing import List, Optional, Type
1
+ from typing import Optional, Type
2
2
 
3
3
  from ninja import Schema
4
4
  from ninja_aio.types import ModelSerializerMeta
@@ -79,12 +79,4 @@ class QuerySchema(ModelQuerySetSchema):
79
79
 
80
80
  class QueryUtilBaseScopesSchema(BaseModel):
81
81
  READ: str = "read"
82
- QUERYSET_REQUEST: str = "queryset_request"
83
-
84
-
85
- class DecoratorsSchema(Schema):
86
- list: Optional[List] = []
87
- retrieve: Optional[List] = []
88
- create: Optional[List] = []
89
- update: Optional[List] = []
90
- delete: Optional[List] = []
82
+ QUERYSET_REQUEST: str = "queryset_request"
ninja_aio/views.py CHANGED
@@ -7,7 +7,7 @@ from django.http import HttpRequest
7
7
  from django.db.models import Model, QuerySet
8
8
  from pydantic import create_model
9
9
 
10
- from ninja_aio.schemas.helpers import ModelQuerySetSchema, QuerySchema, DecoratorsSchema
10
+ from ninja_aio.schemas.helpers import ModelQuerySetSchema, QuerySchema
11
11
 
12
12
  from .models import ModelSerializer, ModelUtil
13
13
  from .schemas import (
@@ -16,7 +16,7 @@ from .schemas import (
16
16
  )
17
17
  from .helpers.api import ManyToManyAPI
18
18
  from .types import ModelSerializerMeta, VIEW_TYPES
19
- from .decorators import unique_view, decorate_view
19
+ from .decorators import unique_view
20
20
 
21
21
  ERROR_CODES = frozenset({400, 401, 404, 428})
22
22
 
@@ -158,7 +158,6 @@ class APIViewSet:
158
158
 
159
159
  model: ModelSerializer | Model
160
160
  api: NinjaAPI
161
- router_tag: str = ""
162
161
  schema_in: Schema | None = None
163
162
  schema_out: Schema | None = None
164
163
  schema_update: Schema | None = None
@@ -178,7 +177,6 @@ class APIViewSet:
178
177
  delete_docs = "Delete an object by its primary key."
179
178
  m2m_relations: list[M2MRelationSchema] = []
180
179
  m2m_auth: list | None = NOT_SET
181
- extra_decorators: DecoratorsSchema = DecoratorsSchema()
182
180
 
183
181
  def __init__(self) -> None:
184
182
  self.error_codes = ERROR_CODES
@@ -191,9 +189,7 @@ class APIViewSet:
191
189
  self.path_schema = self._generate_path_schema()
192
190
  self.filters_schema = self._generate_filters_schema()
193
191
  self.model_verbose_name = self.model._meta.verbose_name.capitalize()
194
- self.router_tag = (
195
- self.model_verbose_name if not self.router_tag else self.router_tag
196
- )
192
+ self.router_tag = self.model_verbose_name
197
193
  self.router = Router(tags=[self.router_tag])
198
194
  self.path = "/"
199
195
  self.get_path = ""
@@ -310,7 +306,7 @@ class APIViewSet:
310
306
  description=self.create_docs,
311
307
  response={201: self.schema_out, self.error_codes: GenericMessageSchema},
312
308
  )
313
- @decorate_view(unique_view(self), *self.extra_decorators.create)
309
+ @unique_view(self)
314
310
  async def create(request: HttpRequest, data: self.schema_in): # type: ignore
315
311
  return 201, await self.model_util.create_s(request, data, self.schema_out)
316
312
 
@@ -331,11 +327,8 @@ class APIViewSet:
331
327
  self.error_codes: GenericMessageSchema,
332
328
  },
333
329
  )
334
- @decorate_view(
335
- paginate(self.pagination_class),
336
- unique_view(self, plural=True),
337
- *self.extra_decorators.list,
338
- )
330
+ @unique_view(self, plural=True)
331
+ @paginate(self.pagination_class)
339
332
  async def list(
340
333
  request: HttpRequest,
341
334
  filters: Query[self.filters_schema] = None, # type: ignore
@@ -363,7 +356,7 @@ class APIViewSet:
363
356
  description=self.retrieve_docs,
364
357
  response={200: self.schema_out, self.error_codes: GenericMessageSchema},
365
358
  )
366
- @decorate_view(unique_view(self), *self.extra_decorators.retrieve)
359
+ @unique_view(self)
367
360
  async def retrieve(request: HttpRequest, pk: Path[self.path_schema]): # type: ignore
368
361
  query_data = self._get_query_data()
369
362
  return await self.model_util.read_s(
@@ -388,7 +381,7 @@ class APIViewSet:
388
381
  description=self.update_docs,
389
382
  response={200: self.schema_out, self.error_codes: GenericMessageSchema},
390
383
  )
391
- @decorate_view(unique_view(self), *self.extra_decorators.update)
384
+ @unique_view(self)
392
385
  async def update(
393
386
  request: HttpRequest,
394
387
  data: self.schema_update, # type: ignore
@@ -412,7 +405,7 @@ class APIViewSet:
412
405
  description=self.delete_docs,
413
406
  response={204: None, self.error_codes: GenericMessageSchema},
414
407
  )
415
- @decorate_view(unique_view(self), *self.extra_decorators.delete)
408
+ @unique_view(self)
416
409
  async def delete(request: HttpRequest, pk: Path[self.path_schema]): # type: ignore
417
410
  return 204, await self.model_util.delete_s(request, self._get_pk(pk))
418
411