django-ninja-aio-crud 0.1.4__py3-none-any.whl → 0.2.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-ninja-aio-crud
3
- Version: 0.1.4
3
+ Version: 0.2.1
4
4
  Summary: Django Ninja AIO CRUD - Rest Framework
5
5
  Author: Giuseppe Casillo
6
6
  Requires-Python: >=3.10
@@ -130,20 +130,21 @@ class Foo(ModelSerializer):
130
130
 
131
131
  - post create method is a custom method that comes out to handle actions which will be excuted after that the object is created. It can be used, indeed, for example to handle custom fields' actions.
132
132
 
133
+
133
134
  ### APIViewSet
134
135
 
135
- - View class used to automatically generate CRUD views. in your views.py import APIViewSet and define your api using NinjaAPI class. As Parser and Render of the API you must use ninja_aio built-in classes which will serialize data using orjson.
136
+ - View class used to automatically generate CRUD views. in your views.py import APIViewSet and define your api using NinjaAIO class. NinjaAIO class uses built-in parser and renderer which use orjson for data serialization.
136
137
 
137
138
  ```Python
138
139
  # views.py
139
- from ninja import NinjAPI
140
+ from ninja_aio import NinjaAIO
140
141
  from ninja_aio.views import APIViewSet
141
142
  from ninja_aio.parsers import ORJSONParser
142
143
  from ninja_aio.renders import ORJSONRender
143
144
 
144
145
  from .models import Foo
145
146
 
146
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
147
+ api = NinjaAIO()
147
148
 
148
149
 
149
150
  class FooAPI(APIViewSet):
@@ -158,14 +159,15 @@ FooAPI().add_views_to_route()
158
159
 
159
160
  ```Python
160
161
  # views.py
161
- from ninja import NinjAPI, Schema
162
+ from ninja import Schema
163
+ from ninja_aio import NinjaAIO
162
164
  from ninja_aio.views import APIViewSet
163
165
  from ninja_aio.parsers import ORJSONParser
164
166
  from ninja_aio.renders import ORJSONRender
165
167
 
166
168
  from .models import Foo
167
169
 
168
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
170
+ api = NinjaAIO()
169
171
 
170
172
 
171
173
  class ExampleSchemaOut(Schema):
@@ -196,12 +198,13 @@ FooAPI().add_views_to_route()
196
198
 
197
199
  ```Python
198
200
  # views.py
199
- from ninja import NinjAPI, Schema
201
+ from ninja import Schema
202
+ from ninja_aio import NinjaAIO
200
203
  from ninja_aio.views import APIView
201
204
  from ninja_aio.parsers import ORJSONParser
202
205
  from ninja_aio.renders import ORJSONRender
203
206
 
204
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
207
+ api = NinjaAIO()
205
208
 
206
209
 
207
210
  class ExampleSchemaOut(Schema):
@@ -228,10 +231,7 @@ SumView().add_views_to_route()
228
231
  ```
229
232
 
230
233
  ### Relations
231
- - You can also set ForeignKey and OneToOne relations into serialization(reverse relations are supported too). Django ninja aio crud will serialize every of these relation automatically.
232
-
233
- > [!WARNING]
234
- > Only ForeignKey and OneToOne relations are supported for serialization, ManyToMany relations are not supported yet.
234
+ - You can also set ForeignKey, OneToOne and ManyToMany relations into serialization(reverse relations are supported too). Django ninja aio crud will serialize every of these relation automatically.
235
235
 
236
236
  - Define models:
237
237
 
@@ -270,14 +270,14 @@ class Foo(ModelSerializer):
270
270
 
271
271
  ```Python
272
272
  # views.py
273
- from ninja import NinjAPI
273
+ from ninja_aio import NinjaAIO
274
274
  from ninja_aio.views import APIViewSet
275
275
  from ninja_aio.parsers import ORJSONParser
276
276
  from ninja_aio.renders import ORJSONRender
277
277
 
278
278
  from .models import Foo, Bar
279
279
 
280
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
280
+ api = NinjaAIO()
281
281
 
282
282
 
283
283
  class FooAPI(APIViewSet):
@@ -336,14 +336,15 @@ class CustomJWTBearer(AsyncJWTBearer):
336
336
 
337
337
  ```Python
338
338
  # views.py
339
- from ninja import NinjAPI, Schema
339
+ from ninja import Schema
340
+ from ninja_aio import NinjaAIO
340
341
  from ninja_aio.views import APIViewSet, APIView
341
342
  from ninja_aio.parsers import ORJSONParser
342
343
  from ninja_aio.renders import ORJSONRender
343
344
 
344
345
  from .models import Foo
345
346
 
346
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
347
+ api = NinjaAIO()
347
348
 
348
349
 
349
350
  class FooAPI(APIViewSet):
@@ -0,0 +1,12 @@
1
+ ninja_aio/__init__.py,sha256=wVH_cPllWb0Bdyr7atTEbTJxhDdYsDjbn1CVCeB_e60,120
2
+ ninja_aio/api.py,sha256=qEnCOvviJGfg28N9R2XWzykLGOFpJ0JMqe3rm4AYDkM,1564
3
+ ninja_aio/auth.py,sha256=hGgiblvffpHmmakjaxdNT3G0tq39-Bvc5oLNqjn4qd8,1300
4
+ ninja_aio/exceptions.py,sha256=PPNr1CdC7M7Kx1MtiBGuR4hATYQqEuvxCRU6uSG7rgM,543
5
+ ninja_aio/models.py,sha256=xUZ0998bYkWumwZgnVC7P-CLdbfTzFflmJVbusU2UMY,11276
6
+ ninja_aio/parsers.py,sha256=e_4lGCPV7zs-HTqtdJTc8yQD2KPAn9njbL8nF_Mmgkc,153
7
+ ninja_aio/renders.py,sha256=wLQisbIsMJlGYJMO4Ud-qiaVCvz4yxiZ4mYommixAeQ,1453
8
+ ninja_aio/schemas.py,sha256=EgRkfhnzZqwGvdBmqlZixMtMcoD1ZxV_qzJ3fmaAy20,113
9
+ ninja_aio/views.py,sha256=ejn6adCe91YKo0-0bRePbuARHkz8YWMmP54WxQLLqxY,6322
10
+ django_ninja_aio_crud-0.2.1.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
11
+ django_ninja_aio_crud-0.2.1.dist-info/METADATA,sha256=cB1AOW4xZ0DLUpgMAF6AMJVYP48aKy3xQZowVsFWP0g,10135
12
+ django_ninja_aio_crud-0.2.1.dist-info/RECORD,,
ninja_aio/__init__.py CHANGED
@@ -1,3 +1,7 @@
1
1
  """ Django Ninja AIO CRUD - Rest Framework """
2
2
 
3
- __version__ = "0.1.4"
3
+ __version__ = "0.2.1"
4
+
5
+ from .api import NinjaAIO
6
+
7
+ __all__ = ["NinjaAIO"]
ninja_aio/api.py ADDED
@@ -0,0 +1,48 @@
1
+ from typing import Any, Sequence
2
+
3
+ from ninja.router import Router
4
+ from ninja.throttling import BaseThrottle
5
+ from ninja import NinjaAPI
6
+ from ninja.openapi.docs import DocsBase, Swagger
7
+ from ninja.constants import NOT_SET, NOT_SET_TYPE
8
+
9
+ from ninja_aio.parsers import ORJSONParser
10
+ from ninja_aio.renders import ORJSONRenderer
11
+
12
+
13
+ class NinjaAIO(NinjaAPI):
14
+ def __init__(
15
+ self,
16
+ title: str = "NinjaAPI",
17
+ version: str = "1.0.0",
18
+ description: str = "",
19
+ openapi_url: str | None = "/openapi.json",
20
+ docs: DocsBase = Swagger(),
21
+ docs_url: str | None = "/docs",
22
+ docs_decorator = None,
23
+ servers: list[dict[str, Any]] | None = None,
24
+ urls_namespace: str | None = None,
25
+ csrf: bool = False,
26
+ auth: Sequence[Any]| NOT_SET_TYPE = NOT_SET,
27
+ throttle: BaseThrottle | list[BaseThrottle] | NOT_SET_TYPE = NOT_SET,
28
+ default_router: Router | None = None,
29
+ openapi_extra: dict[str, Any] | None = None
30
+ ):
31
+ super().__init__(
32
+ title=title,
33
+ version=version,
34
+ description=description,
35
+ openapi_url=openapi_url,
36
+ docs=docs,
37
+ docs_url=docs_url,
38
+ docs_decorator=docs_decorator,
39
+ servers=servers,
40
+ urls_namespace=urls_namespace,
41
+ csrf=csrf,
42
+ auth=auth,
43
+ throttle=throttle,
44
+ default_router=default_router,
45
+ openapi_extra=openapi_extra,
46
+ renderer=ORJSONRenderer(),
47
+ parser=ORJSONParser(),
48
+ )
ninja_aio/models.py CHANGED
@@ -7,10 +7,10 @@ from ninja.orm import create_schema
7
7
  from django.db import models
8
8
  from django.http import HttpResponse, HttpRequest
9
9
  from django.core.exceptions import ObjectDoesNotExist
10
- from django.db.models.fields.related import OneToOneRel
11
10
  from django.db.models.fields.related_descriptors import (
12
11
  ReverseManyToOneDescriptor,
13
12
  ReverseOneToOneDescriptor,
13
+ ManyToManyDescriptor,
14
14
  )
15
15
 
16
16
  from .exceptions import SerializeError
@@ -46,6 +46,10 @@ class ModelSerializer(models.Model):
46
46
  def has_custom_fields(self):
47
47
  return self.has_custom_fields_create or self.has_custom_fields_update
48
48
 
49
+ @classmethod
50
+ def verbose_name_path_resolver(cls) -> str:
51
+ return "-".join(cls._meta.verbose_name_plural.split(" "))
52
+
49
53
  def has_changed(self, field: str) -> bool:
50
54
  """
51
55
  Check if a model field has changed
@@ -86,10 +90,14 @@ class ModelSerializer(models.Model):
86
90
  reverse_rels = []
87
91
  for f in cls.ReadSerializer.fields:
88
92
  field_obj = getattr(cls, f)
93
+ if isinstance(field_obj, ManyToManyDescriptor):
94
+ reverse_rels.append(f)
95
+ continue
89
96
  if isinstance(field_obj, ReverseManyToOneDescriptor):
90
97
  reverse_rels.append(field_obj.field._related_name)
98
+ continue
91
99
  if isinstance(field_obj, ReverseOneToOneDescriptor):
92
- reverse_rels.append(list(field_obj[0].related_name))
100
+ reverse_rels.append(field_obj.related.name)
93
101
  return reverse_rels
94
102
 
95
103
  @classmethod
@@ -98,14 +106,24 @@ class ModelSerializer(models.Model):
98
106
  ):
99
107
  cls_f = []
100
108
  for rel_f in obj.ReadSerializer.fields:
101
- rel_f_obj = getattr(obj, rel_f).field
109
+ rel_f_obj = getattr(obj, rel_f)
102
110
  if (
103
- isinstance(rel_f_obj, (models.ForeignKey, models.OneToOneField))
104
- and rel_f_obj.related_model == cls
111
+ isinstance(
112
+ rel_f_obj.field,
113
+ (
114
+ models.ForeignKey,
115
+ models.OneToOneField,
116
+ ),
117
+ )
118
+ and rel_f_obj.field.related_model == cls
105
119
  ):
106
120
  cls_f.append(rel_f)
107
121
  obj.ReadSerializer.fields.remove(rel_f)
108
- break
122
+ continue
123
+ if isinstance(rel_f_obj.field, models.ManyToManyField):
124
+ cls_f.append(rel_f)
125
+ obj.ReadSerializer.fields.remove(rel_f)
126
+
109
127
  rel_schema = obj.generate_read_s(depth=0)
110
128
  if rel_type == "many":
111
129
  rel_schema = list[rel_schema]
@@ -124,13 +142,20 @@ class ModelSerializer(models.Model):
124
142
  reverse_rels = []
125
143
  for f in cls.ReadSerializer.fields:
126
144
  field_obj = getattr(cls, f)
145
+ if isinstance(field_obj, ManyToManyDescriptor):
146
+ rel_obj: ModelSerializer = field_obj.field.related_model
147
+ if field_obj.reverse:
148
+ rel_obj: ModelSerializer = field_obj.field.model
149
+ rel_data = cls.get_reverse_relation_schema(rel_obj, "many", f)
150
+ reverse_rels.append(rel_data)
151
+ continue
127
152
  if isinstance(field_obj, ReverseManyToOneDescriptor):
128
153
  rel_obj: ModelSerializer = field_obj.field.model
129
154
  rel_data = cls.get_reverse_relation_schema(rel_obj, "many", f)
130
155
  reverse_rels.append(rel_data)
131
156
  continue
132
157
  if isinstance(field_obj, ReverseOneToOneDescriptor):
133
- rel_obj: ModelSerializer = list(field_obj)[0].related_model
158
+ rel_obj: ModelSerializer = field_obj.related.related_model
134
159
  rel_data = cls.get_reverse_relation_schema(rel_obj, "one", f)
135
160
  reverse_rels.append(rel_data)
136
161
  continue
@@ -178,7 +203,7 @@ class ModelSerializer(models.Model):
178
203
  field_obj = getattr(cls, k).related
179
204
  if isinstance(v, dict) and (
180
205
  isinstance(field_obj, models.ForeignKey)
181
- or isinstance(field_obj, OneToOneRel)
206
+ or isinstance(field_obj, models.OneToOneField)
182
207
  ):
183
208
  rel: ModelSerializer = await field_obj.related_model.get_object(
184
209
  request, list(v.values())[0]
@@ -280,10 +305,10 @@ class ModelSerializer(models.Model):
280
305
  for k, v in payload.items():
281
306
  if v is not None:
282
307
  setattr(obj, k, v)
283
- payload |= customs
284
- await obj.custom_actions(payload)
308
+ await obj.custom_actions(customs)
285
309
  await obj.asave()
286
- return await cls.read_s(request, obj)
310
+ updated_object = await cls.get_object(request, pk)
311
+ return await cls.read_s(request, updated_object)
287
312
 
288
313
  @classmethod
289
314
  async def delete_s(cls, request: HttpRequest, pk: int | str):
ninja_aio/views.py CHANGED
@@ -192,6 +192,6 @@ class APIViewSet:
192
192
 
193
193
  def add_views_to_route(self):
194
194
  return self.api.add_router(
195
- f"{self.model._meta.verbose_name_plural}/",
195
+ f"{self.model.verbose_name_path_resolver()}/",
196
196
  self.add_views(),
197
197
  )
@@ -1,11 +0,0 @@
1
- ninja_aio/__init__.py,sha256=ezfuNggZR9pW5CRIDO17RBPn9EANhL9Rz5SZVc2_7OM,70
2
- ninja_aio/auth.py,sha256=hGgiblvffpHmmakjaxdNT3G0tq39-Bvc5oLNqjn4qd8,1300
3
- ninja_aio/exceptions.py,sha256=PPNr1CdC7M7Kx1MtiBGuR4hATYQqEuvxCRU6uSG7rgM,543
4
- ninja_aio/models.py,sha256=m_agSUXIEbt_qOHQhc1G2t-W5-jTofquZDpiBf2y8so,10293
5
- ninja_aio/parsers.py,sha256=e_4lGCPV7zs-HTqtdJTc8yQD2KPAn9njbL8nF_Mmgkc,153
6
- ninja_aio/renders.py,sha256=wLQisbIsMJlGYJMO4Ud-qiaVCvz4yxiZ4mYommixAeQ,1453
7
- ninja_aio/schemas.py,sha256=EgRkfhnzZqwGvdBmqlZixMtMcoD1ZxV_qzJ3fmaAy20,113
8
- ninja_aio/views.py,sha256=j72V0GHlTNdFDsO2f8ZMy_0aS7Hb4CKWN1t_st1ohDM,6319
9
- django_ninja_aio_crud-0.1.4.dist-info/WHEEL,sha256=EZbGkh7Ie4PoZfRQ8I0ZuP9VklN_TvcZ6DSE5Uar4z4,81
10
- django_ninja_aio_crud-0.1.4.dist-info/METADATA,sha256=ejUKHqdUYxzqDHQOidGtEnOCe2shkLnLCH2ok7t9hac,10440
11
- django_ninja_aio_crud-0.1.4.dist-info/RECORD,,