django-ninja-aio-crud 0.1.4__tar.gz → 0.2.1__tar.gz

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):
@@ -101,20 +101,21 @@ class Foo(ModelSerializer):
101
101
 
102
102
  - 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.
103
103
 
104
+
104
105
  ### APIViewSet
105
106
 
106
- - 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.
107
+ - 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.
107
108
 
108
109
  ```Python
109
110
  # views.py
110
- from ninja import NinjAPI
111
+ from ninja_aio import NinjaAIO
111
112
  from ninja_aio.views import APIViewSet
112
113
  from ninja_aio.parsers import ORJSONParser
113
114
  from ninja_aio.renders import ORJSONRender
114
115
 
115
116
  from .models import Foo
116
117
 
117
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
118
+ api = NinjaAIO()
118
119
 
119
120
 
120
121
  class FooAPI(APIViewSet):
@@ -129,14 +130,15 @@ FooAPI().add_views_to_route()
129
130
 
130
131
  ```Python
131
132
  # views.py
132
- from ninja import NinjAPI, Schema
133
+ from ninja import Schema
134
+ from ninja_aio import NinjaAIO
133
135
  from ninja_aio.views import APIViewSet
134
136
  from ninja_aio.parsers import ORJSONParser
135
137
  from ninja_aio.renders import ORJSONRender
136
138
 
137
139
  from .models import Foo
138
140
 
139
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
141
+ api = NinjaAIO()
140
142
 
141
143
 
142
144
  class ExampleSchemaOut(Schema):
@@ -167,12 +169,13 @@ FooAPI().add_views_to_route()
167
169
 
168
170
  ```Python
169
171
  # views.py
170
- from ninja import NinjAPI, Schema
172
+ from ninja import Schema
173
+ from ninja_aio import NinjaAIO
171
174
  from ninja_aio.views import APIView
172
175
  from ninja_aio.parsers import ORJSONParser
173
176
  from ninja_aio.renders import ORJSONRender
174
177
 
175
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
178
+ api = NinjaAIO()
176
179
 
177
180
 
178
181
  class ExampleSchemaOut(Schema):
@@ -199,10 +202,7 @@ SumView().add_views_to_route()
199
202
  ```
200
203
 
201
204
  ### Relations
202
- - 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.
203
-
204
- > [!WARNING]
205
- > Only ForeignKey and OneToOne relations are supported for serialization, ManyToMany relations are not supported yet.
205
+ - 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.
206
206
 
207
207
  - Define models:
208
208
 
@@ -241,14 +241,14 @@ class Foo(ModelSerializer):
241
241
 
242
242
  ```Python
243
243
  # views.py
244
- from ninja import NinjAPI
244
+ from ninja_aio import NinjaAIO
245
245
  from ninja_aio.views import APIViewSet
246
246
  from ninja_aio.parsers import ORJSONParser
247
247
  from ninja_aio.renders import ORJSONRender
248
248
 
249
249
  from .models import Foo, Bar
250
250
 
251
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
251
+ api = NinjaAIO()
252
252
 
253
253
 
254
254
  class FooAPI(APIViewSet):
@@ -307,14 +307,15 @@ class CustomJWTBearer(AsyncJWTBearer):
307
307
 
308
308
  ```Python
309
309
  # views.py
310
- from ninja import NinjAPI, Schema
310
+ from ninja import Schema
311
+ from ninja_aio import NinjaAIO
311
312
  from ninja_aio.views import APIViewSet, APIView
312
313
  from ninja_aio.parsers import ORJSONParser
313
314
  from ninja_aio.renders import ORJSONRender
314
315
 
315
316
  from .models import Foo
316
317
 
317
- api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
318
+ api = NinjaAIO()
318
319
 
319
320
 
320
321
  class FooAPI(APIViewSet):
@@ -0,0 +1,7 @@
1
+ """ Django Ninja AIO CRUD - Rest Framework """
2
+
3
+ __version__ = "0.2.1"
4
+
5
+ from .api import NinjaAIO
6
+
7
+ __all__ = ["NinjaAIO"]
@@ -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
+ )
@@ -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):
@@ -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,3 +0,0 @@
1
- """ Django Ninja AIO CRUD - Rest Framework """
2
-
3
- __version__ = "0.1.4"