django-ninja-aio-crud 0.3.1__py3-none-any.whl → 0.4.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.
- {django_ninja_aio_crud-0.3.1.dist-info → django_ninja_aio_crud-0.4.0.dist-info}/METADATA +34 -12
- django_ninja_aio_crud-0.4.0.dist-info/RECORD +13 -0
- ninja_aio/__init__.py +3 -3
- ninja_aio/api.py +9 -4
- ninja_aio/auth.py +0 -1
- ninja_aio/exceptions.py +17 -0
- ninja_aio/models.py +77 -142
- ninja_aio/types.py +1 -0
- ninja_aio/views.py +1 -5
- django_ninja_aio_crud-0.3.1.dist-info/RECORD +0 -13
- {django_ninja_aio_crud-0.3.1.dist-info → django_ninja_aio_crud-0.4.0.dist-info}/WHEEL +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: django-ninja-aio-crud
|
|
3
|
-
Version: 0.
|
|
4
|
-
Summary: Django Ninja AIO CRUD - Rest Framework
|
|
3
|
+
Version: 0.4.0
|
|
4
|
+
Summary: Django Ninja AIO CRUD - Rest Framework
|
|
5
5
|
Author: Giuseppe Casillo
|
|
6
6
|
Requires-Python: >=3.10
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
@@ -73,7 +73,7 @@ pip install django-ninja-aio-crud
|
|
|
73
73
|
### ModelSerializer
|
|
74
74
|
|
|
75
75
|
- You can serialize your models using ModelSerializer and made them inherit from it. In your models.py import ModelSerializer
|
|
76
|
-
```
|
|
76
|
+
```python
|
|
77
77
|
# models.py
|
|
78
78
|
from django.db import models
|
|
79
79
|
from ninja_aio.models import ModelSerializer
|
|
@@ -95,7 +95,7 @@ class Foo(ModelSerializer):
|
|
|
95
95
|
|
|
96
96
|
- ReadSerializer, CreateSerializer, UpdateSerializer are used to define which fields would be included in runtime schemas creation. You can also specify custom fields and handle their function by overriding custom_actions ModelSerializer's method(custom fields are only available for Create and Update serializers).
|
|
97
97
|
|
|
98
|
-
```
|
|
98
|
+
```python
|
|
99
99
|
# models.py
|
|
100
100
|
from django.db import models
|
|
101
101
|
from ninja_aio.models import ModelSerializer
|
|
@@ -130,12 +130,34 @@ 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
|
+
- You can also define optional fields for you Create and Update serializers (remember to give your optional fields a default). To declare an optional fields you have to give the field type too.
|
|
134
|
+
```python
|
|
135
|
+
# models.py
|
|
136
|
+
from django.db import models
|
|
137
|
+
from ninja_aio.models import ModelSerializer
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
class Foo(ModelSerializer):
|
|
141
|
+
name = models.CharField(max_length=30)
|
|
142
|
+
bar = models.CharField(max_length=30, default="")
|
|
143
|
+
active = models.BooleanField(default=False)
|
|
144
|
+
|
|
145
|
+
class ReadSerializer:
|
|
146
|
+
fields = ["id", "name", "bar"]
|
|
147
|
+
|
|
148
|
+
class CreateSerializer:
|
|
149
|
+
fields = ["name"]
|
|
150
|
+
optionals = [("bar", str), ("active", bool)]
|
|
151
|
+
|
|
152
|
+
class UpdateSerializer:
|
|
153
|
+
optionals = [[("bar", str), ("active", bool)]
|
|
154
|
+
```
|
|
133
155
|
|
|
134
156
|
### APIViewSet
|
|
135
157
|
|
|
136
158
|
- 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.
|
|
137
159
|
|
|
138
|
-
```
|
|
160
|
+
```python
|
|
139
161
|
# views.py
|
|
140
162
|
from ninja_aio import NinjaAIO
|
|
141
163
|
from ninja_aio.views import APIViewSet
|
|
@@ -157,7 +179,7 @@ FooAPI().add_views_to_route()
|
|
|
157
179
|
|
|
158
180
|
- and that's it, your model CRUD will be automatically created. You can also add custom views to CRUD overriding the built-in method "views".
|
|
159
181
|
|
|
160
|
-
```
|
|
182
|
+
```python
|
|
161
183
|
# views.py
|
|
162
184
|
from ninja import Schema
|
|
163
185
|
from ninja_aio import NinjaAIO
|
|
@@ -196,7 +218,7 @@ FooAPI().add_views_to_route()
|
|
|
196
218
|
|
|
197
219
|
- View class to code generic views class based. In your views.py import APIView class.
|
|
198
220
|
|
|
199
|
-
```
|
|
221
|
+
```python
|
|
200
222
|
# views.py
|
|
201
223
|
from ninja import Schema
|
|
202
224
|
from ninja_aio import NinjaAIO
|
|
@@ -235,7 +257,7 @@ SumView().add_views_to_route()
|
|
|
235
257
|
|
|
236
258
|
- Define models:
|
|
237
259
|
|
|
238
|
-
```
|
|
260
|
+
```python
|
|
239
261
|
# models.py
|
|
240
262
|
class Bar(ModelSerializer):
|
|
241
263
|
name = models.CharField(max_length=30)
|
|
@@ -268,7 +290,7 @@ class Foo(ModelSerializer):
|
|
|
268
290
|
|
|
269
291
|
- Define views:
|
|
270
292
|
|
|
271
|
-
```
|
|
293
|
+
```python
|
|
272
294
|
# views.py
|
|
273
295
|
from ninja_aio import NinjaAIO
|
|
274
296
|
from ninja_aio.views import APIViewSet
|
|
@@ -312,7 +334,7 @@ BarAPI().add_views_to_route()
|
|
|
312
334
|
|
|
313
335
|
- AsyncJWTBearer built-in class is an authenticator class which use joserfc module. It cames out with authenticate method which validate given claims. Override auth handler method to write your own authentication method. Default algorithms used is RS256. a jwt Token istance is set as class atribute so you can use it by self.dcd.
|
|
314
336
|
|
|
315
|
-
```
|
|
337
|
+
```python
|
|
316
338
|
from ninja_aio.auth import AsyncJWTBearer
|
|
317
339
|
from django.conf import settings
|
|
318
340
|
from django.http import HttpRequest
|
|
@@ -334,7 +356,7 @@ class CustomJWTBearer(AsyncJWTBearer):
|
|
|
334
356
|
|
|
335
357
|
- Then add it to views.
|
|
336
358
|
|
|
337
|
-
```
|
|
359
|
+
```python
|
|
338
360
|
# views.py
|
|
339
361
|
from ninja import Schema
|
|
340
362
|
from ninja_aio import NinjaAIO
|
|
@@ -382,7 +404,7 @@ SumView().add_views_to_route()
|
|
|
382
404
|
|
|
383
405
|
- By default APIViewSet list view uses Django Ninja built-in AsyncPagination class "PageNumberPagination". You can customize and assign it to APIViewSet class. To make your custom pagination consult **<a href="https://django-ninja.dev/guides/response/pagination/#async-pagination">Django Ninja pagination documentation</a>**.
|
|
384
406
|
|
|
385
|
-
```
|
|
407
|
+
```python
|
|
386
408
|
# views.py
|
|
387
409
|
|
|
388
410
|
class FooAPI(APIViewSet):
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
ninja_aio/__init__.py,sha256=GPvxFosuQ4fZ9LpbRClXqv8e7BecmVUtj2nvbJyalus,119
|
|
2
|
+
ninja_aio/api.py,sha256=Fe6l3YCy7MW5TY4-Lbl80CFuK2NT2Y7tHfmqPk6Mqak,1735
|
|
3
|
+
ninja_aio/auth.py,sha256=3Pr8llYoCN59ZH3J_2qWmzjXxsy-rpQzXrVfwLfY25Q,1299
|
|
4
|
+
ninja_aio/exceptions.py,sha256=LP9GZpDk1fYMRRgGHSAstcCvgu5uSDLg8PPDANlZGTs,1008
|
|
5
|
+
ninja_aio/models.py,sha256=mIZofuGOsRATYQki7GIuz50OQWfMes41cvOOAAonQA0,13768
|
|
6
|
+
ninja_aio/parsers.py,sha256=e_4lGCPV7zs-HTqtdJTc8yQD2KPAn9njbL8nF_Mmgkc,153
|
|
7
|
+
ninja_aio/renders.py,sha256=mHeKNJtmDhZmgFpS9B6SPn5uZFcyVXrsoMhr149LeW8,1555
|
|
8
|
+
ninja_aio/schemas.py,sha256=EgRkfhnzZqwGvdBmqlZixMtMcoD1ZxV_qzJ3fmaAy20,113
|
|
9
|
+
ninja_aio/types.py,sha256=ZhFqRDP5g2A2er3izx36QjrYJxL_jgusbH7w7mVHNrk,339
|
|
10
|
+
ninja_aio/views.py,sha256=IT6rTCcjXcVYrCi4_uEwnGOGXeWlIB4ZuyXJp8Fsqqc,6971
|
|
11
|
+
django_ninja_aio_crud-0.4.0.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
12
|
+
django_ninja_aio_crud-0.4.0.dist-info/METADATA,sha256=0yQr4CWufJ7ZtXuhpTOchKQ-yOzG7o-wTaymDV0Tqvc,10832
|
|
13
|
+
django_ninja_aio_crud-0.4.0.dist-info/RECORD,,
|
ninja_aio/__init__.py
CHANGED
ninja_aio/api.py
CHANGED
|
@@ -8,6 +8,7 @@ from ninja.constants import NOT_SET, NOT_SET_TYPE
|
|
|
8
8
|
|
|
9
9
|
from .parsers import ORJSONParser
|
|
10
10
|
from .renders import ORJSONRenderer
|
|
11
|
+
from .exceptions import set_api_exception_handlers
|
|
11
12
|
|
|
12
13
|
|
|
13
14
|
class NinjaAIO(NinjaAPI):
|
|
@@ -19,14 +20,14 @@ class NinjaAIO(NinjaAPI):
|
|
|
19
20
|
openapi_url: str | None = "/openapi.json",
|
|
20
21
|
docs: DocsBase = Swagger(),
|
|
21
22
|
docs_url: str | None = "/docs",
|
|
22
|
-
docs_decorator
|
|
23
|
+
docs_decorator=None,
|
|
23
24
|
servers: list[dict[str, Any]] | None = None,
|
|
24
25
|
urls_namespace: str | None = None,
|
|
25
26
|
csrf: bool = False,
|
|
26
|
-
auth: Sequence[Any]| NOT_SET_TYPE = NOT_SET,
|
|
27
|
+
auth: Sequence[Any] | NOT_SET_TYPE = NOT_SET,
|
|
27
28
|
throttle: BaseThrottle | list[BaseThrottle] | NOT_SET_TYPE = NOT_SET,
|
|
28
29
|
default_router: Router | None = None,
|
|
29
|
-
openapi_extra: dict[str, Any] | None = None
|
|
30
|
+
openapi_extra: dict[str, Any] | None = None,
|
|
30
31
|
):
|
|
31
32
|
super().__init__(
|
|
32
33
|
title=title,
|
|
@@ -45,4 +46,8 @@ class NinjaAIO(NinjaAPI):
|
|
|
45
46
|
openapi_extra=openapi_extra,
|
|
46
47
|
renderer=ORJSONRenderer(),
|
|
47
48
|
parser=ORJSONParser(),
|
|
48
|
-
)
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
def set_default_exception_handlers(self):
|
|
52
|
+
set_api_exception_handlers(self)
|
|
53
|
+
super().set_default_exception_handlers()
|
ninja_aio/auth.py
CHANGED
ninja_aio/exceptions.py
CHANGED
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
from functools import partial
|
|
2
|
+
from ninja import NinjaAPI
|
|
3
|
+
from django.http import HttpRequest, HttpResponse
|
|
4
|
+
|
|
5
|
+
|
|
1
6
|
class BaseException(Exception):
|
|
2
7
|
error: str | dict = ""
|
|
3
8
|
status_code: int = 400
|
|
@@ -22,3 +27,15 @@ class SerializeError(BaseException):
|
|
|
22
27
|
|
|
23
28
|
class AuthError(BaseException):
|
|
24
29
|
pass
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
def _default_serialize_error(
|
|
33
|
+
request: HttpRequest, exc: SerializeError, api: "NinjaAPI"
|
|
34
|
+
) -> HttpResponse:
|
|
35
|
+
return api.create_response(request, exc.error, status=exc.status_code)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def set_api_exception_handlers(api: type[NinjaAPI]) -> None:
|
|
39
|
+
api.add_exception_handler(
|
|
40
|
+
SerializeError, partial(_default_serialize_error, api=api)
|
|
41
|
+
)
|
ninja_aio/models.py
CHANGED
|
@@ -14,7 +14,7 @@ from django.db.models.fields.related_descriptors import (
|
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
from .exceptions import SerializeError
|
|
17
|
-
from .types import S_TYPES, REL_TYPES, ModelSerializerMeta
|
|
17
|
+
from .types import S_TYPES, REL_TYPES, F_TYPES, ModelSerializerMeta
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
class ModelUtil:
|
|
@@ -58,11 +58,18 @@ class ModelUtil:
|
|
|
58
58
|
async def parse_input_data(self, request: HttpRequest, data: Schema):
|
|
59
59
|
payload = data.model_dump()
|
|
60
60
|
customs = {}
|
|
61
|
+
optionals = []
|
|
61
62
|
if isinstance(self.model, ModelSerializerMeta):
|
|
62
63
|
customs = {k: v for k, v in payload.items() if self.model.is_custom(k)}
|
|
64
|
+
optionals = [
|
|
65
|
+
k for k, v in payload.items() if self.model.is_optional(k) and v is None
|
|
66
|
+
]
|
|
63
67
|
for k, v in payload.items():
|
|
64
|
-
if isinstance(self.model, ModelSerializerMeta)
|
|
65
|
-
|
|
68
|
+
if isinstance(self.model, ModelSerializerMeta):
|
|
69
|
+
if self.model.is_custom(k):
|
|
70
|
+
continue
|
|
71
|
+
if self.model.is_optional(k) and k is None:
|
|
72
|
+
continue
|
|
66
73
|
field_obj = getattr(self.model, k).field
|
|
67
74
|
if isinstance(field_obj, models.BinaryField):
|
|
68
75
|
try:
|
|
@@ -73,7 +80,9 @@ class ModelUtil:
|
|
|
73
80
|
rel_util = ModelUtil(field_obj.related_model)
|
|
74
81
|
rel: ModelSerializer = await rel_util.get_object(request, v)
|
|
75
82
|
payload |= {k: rel}
|
|
76
|
-
new_payload = {
|
|
83
|
+
new_payload = {
|
|
84
|
+
k: v for k, v in payload.items() if k not in (customs.keys() or optionals)
|
|
85
|
+
}
|
|
77
86
|
return new_payload, customs
|
|
78
87
|
|
|
79
88
|
async def parse_output_data(self, request: HttpRequest, data: Schema):
|
|
@@ -106,12 +115,9 @@ class ModelUtil:
|
|
|
106
115
|
return payload
|
|
107
116
|
|
|
108
117
|
async def create_s(self, request: HttpRequest, data: Schema, obj_schema: Schema):
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
obj = await self.get_object(request, pk)
|
|
113
|
-
except SerializeError as e:
|
|
114
|
-
return e.status_code, e.error
|
|
118
|
+
payload, customs = await self.parse_input_data(request, data)
|
|
119
|
+
pk = (await self.model.objects.acreate(**payload)).pk
|
|
120
|
+
obj = await self.get_object(request, pk)
|
|
115
121
|
if isinstance(self.model, ModelSerializerMeta):
|
|
116
122
|
await obj.custom_actions(customs)
|
|
117
123
|
await obj.post_create()
|
|
@@ -130,11 +136,7 @@ class ModelUtil:
|
|
|
130
136
|
async def update_s(
|
|
131
137
|
self, request: HttpRequest, data: Schema, pk: int | str, obj_schema: Schema
|
|
132
138
|
):
|
|
133
|
-
|
|
134
|
-
obj = await self.get_object(request, pk)
|
|
135
|
-
except SerializeError as e:
|
|
136
|
-
return e.status_code, e.error
|
|
137
|
-
|
|
139
|
+
obj = await self.get_object(request, pk)
|
|
138
140
|
payload, customs = await self.parse_input_data(request, data)
|
|
139
141
|
for k, v in payload.items():
|
|
140
142
|
if v is not None:
|
|
@@ -146,10 +148,7 @@ class ModelUtil:
|
|
|
146
148
|
return await self.read_s(request, updated_object, obj_schema)
|
|
147
149
|
|
|
148
150
|
async def delete_s(self, request: HttpRequest, pk: int | str):
|
|
149
|
-
|
|
150
|
-
obj = await self.get_object(request, pk)
|
|
151
|
-
except SerializeError as e:
|
|
152
|
-
return e.status_code, e.error
|
|
151
|
+
obj = await self.get_object(request, pk)
|
|
153
152
|
await obj.adelete()
|
|
154
153
|
return HttpResponse(status=204)
|
|
155
154
|
|
|
@@ -161,6 +160,7 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
161
160
|
class CreateSerializer:
|
|
162
161
|
fields: list[str] = []
|
|
163
162
|
customs: list[tuple[str, type, Any]] = []
|
|
163
|
+
optionals: list[str] = []
|
|
164
164
|
|
|
165
165
|
class ReadSerializer:
|
|
166
166
|
fields: list[str] = []
|
|
@@ -168,6 +168,7 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
168
168
|
class UpdateSerializer:
|
|
169
169
|
fields: list[str] = []
|
|
170
170
|
customs: list[tuple[str, type, Any]] = []
|
|
171
|
+
optionals: list[str] = []
|
|
171
172
|
|
|
172
173
|
@property
|
|
173
174
|
def has_custom_fields_create(self):
|
|
@@ -181,6 +182,37 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
181
182
|
def has_custom_fields(self):
|
|
182
183
|
return self.has_custom_fields_create or self.has_custom_fields_update
|
|
183
184
|
|
|
185
|
+
@property
|
|
186
|
+
def has_optional_fields_create(self):
|
|
187
|
+
return hasattr(self.CreateSerializer, "optionals")
|
|
188
|
+
|
|
189
|
+
@property
|
|
190
|
+
def has_optional_fields_update(self):
|
|
191
|
+
return hasattr(self.UpdateSerializer, "optionals")
|
|
192
|
+
|
|
193
|
+
@property
|
|
194
|
+
def has_optional_fields(self):
|
|
195
|
+
return self.has_optional_fields_create or self.has_optional_fields_update
|
|
196
|
+
|
|
197
|
+
@classmethod
|
|
198
|
+
def _get_special_fields(cls, s_type: type[S_TYPES], f_type: type[F_TYPES]):
|
|
199
|
+
try:
|
|
200
|
+
match s_type:
|
|
201
|
+
case "create":
|
|
202
|
+
fields = getattr(cls.CreateSerializer, f_type)
|
|
203
|
+
case "update":
|
|
204
|
+
fields = getattr(cls.UpdateSerializer, f_type)
|
|
205
|
+
except AttributeError:
|
|
206
|
+
return []
|
|
207
|
+
return fields
|
|
208
|
+
|
|
209
|
+
@classmethod
|
|
210
|
+
def _is_special_field(
|
|
211
|
+
cls, s_type: type[S_TYPES], field: str, f_type: type[F_TYPES]
|
|
212
|
+
):
|
|
213
|
+
special_fields = cls._get_special_fields(s_type, f_type)
|
|
214
|
+
return any(field in special_f for special_f in special_fields)
|
|
215
|
+
|
|
184
216
|
@classmethod
|
|
185
217
|
def verbose_name_path_resolver(cls) -> str:
|
|
186
218
|
return "-".join(cls._meta.verbose_name_plural.split(" "))
|
|
@@ -284,71 +316,26 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
284
316
|
|
|
285
317
|
@classmethod
|
|
286
318
|
def is_custom(cls, field: str):
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
319
|
+
return cls._is_special_field(
|
|
320
|
+
"create", field, "customs"
|
|
321
|
+
) or cls._is_special_field("update", field, "customs")
|
|
290
322
|
|
|
291
323
|
@classmethod
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
if cls.is_custom(k):
|
|
297
|
-
continue
|
|
298
|
-
field_obj = getattr(cls, k).field
|
|
299
|
-
if isinstance(field_obj, models.BinaryField):
|
|
300
|
-
try:
|
|
301
|
-
payload |= {k: base64.b64decode(v)}
|
|
302
|
-
except Exception as exc:
|
|
303
|
-
raise SerializeError({k: ". ".join(exc.args)}, 400)
|
|
304
|
-
if isinstance(field_obj, models.ForeignKey):
|
|
305
|
-
rel: ModelSerializer = await field_obj.related_model.get_object(
|
|
306
|
-
request, v
|
|
307
|
-
)
|
|
308
|
-
payload |= {k: rel}
|
|
309
|
-
new_payload = {k: v for k, v in payload.items() if k not in customs}
|
|
310
|
-
return new_payload, customs
|
|
324
|
+
def is_optional(cls, field: str):
|
|
325
|
+
return cls._is_special_field(
|
|
326
|
+
"create", field, "optionals"
|
|
327
|
+
) or cls._is_special_field("update", field, "optionals")
|
|
311
328
|
|
|
312
329
|
@classmethod
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
payload = data.model_dump()
|
|
316
|
-
for k, v in payload.items():
|
|
317
|
-
try:
|
|
318
|
-
field_obj = getattr(cls, k).field
|
|
319
|
-
except AttributeError:
|
|
320
|
-
field_obj = getattr(cls, k).related
|
|
321
|
-
if isinstance(v, dict) and (
|
|
322
|
-
isinstance(field_obj, models.ForeignKey)
|
|
323
|
-
or isinstance(field_obj, models.OneToOneField)
|
|
324
|
-
):
|
|
325
|
-
rel: ModelSerializer = await field_obj.related_model.get_object(
|
|
326
|
-
request, list(v.values())[0]
|
|
327
|
-
)
|
|
328
|
-
if isinstance(field_obj, models.ForeignKey):
|
|
329
|
-
for rel_k, rel_v in v.items():
|
|
330
|
-
field_rel_obj = getattr(rel, rel_k)
|
|
331
|
-
if isinstance(field_rel_obj, models.ForeignKey):
|
|
332
|
-
olds_k.append({rel_k: rel_v})
|
|
333
|
-
for obj in olds_k:
|
|
334
|
-
for old_k, old_v in obj.items():
|
|
335
|
-
v.pop(old_k)
|
|
336
|
-
v |= {f"{old_k}_id": old_v}
|
|
337
|
-
olds_k = []
|
|
338
|
-
payload |= {k: rel}
|
|
339
|
-
return payload
|
|
330
|
+
def get_custom_fields(cls, s_type: type[S_TYPES]):
|
|
331
|
+
return cls._get_special_fields(s_type, "customs")
|
|
340
332
|
|
|
341
333
|
@classmethod
|
|
342
|
-
def
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
case "update":
|
|
348
|
-
customs = cls.UpdateSerializer.customs
|
|
349
|
-
except AttributeError:
|
|
350
|
-
return None
|
|
351
|
-
return customs
|
|
334
|
+
def get_optional_fields(cls, s_type: type[S_TYPES]):
|
|
335
|
+
return [
|
|
336
|
+
(field, field_type, None)
|
|
337
|
+
for field, field_type in cls._get_special_fields(s_type, "optionals")
|
|
338
|
+
]
|
|
352
339
|
|
|
353
340
|
@classmethod
|
|
354
341
|
def generate_read_s(cls, depth: int = 1) -> Schema:
|
|
@@ -364,78 +351,26 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
364
351
|
|
|
365
352
|
@classmethod
|
|
366
353
|
def generate_create_s(cls) -> Schema:
|
|
354
|
+
fields = getattr(cls.CreateSerializer, "fields", []) + [
|
|
355
|
+
field[0] for field in cls.get_optional_fields("create")
|
|
356
|
+
]
|
|
357
|
+
customs = cls.get_custom_fields("create") + cls.get_optional_fields("create")
|
|
367
358
|
return create_schema(
|
|
368
359
|
model=cls,
|
|
369
360
|
name=f"{cls._meta.model_name}SchemaIn",
|
|
370
|
-
fields=
|
|
371
|
-
custom_fields=
|
|
361
|
+
fields=fields,
|
|
362
|
+
custom_fields=customs,
|
|
372
363
|
)
|
|
373
364
|
|
|
374
365
|
@classmethod
|
|
375
366
|
def generate_update_s(cls) -> Schema:
|
|
367
|
+
fields = getattr(cls.UpdateSerializer, "fields", []) + [
|
|
368
|
+
field[0] for field in cls.get_optional_fields("update")
|
|
369
|
+
]
|
|
370
|
+
customs = cls.get_custom_fields("update") + cls.get_optional_fields("update")
|
|
376
371
|
return create_schema(
|
|
377
372
|
model=cls,
|
|
378
373
|
name=f"{cls._meta.model_name}SchemaPatch",
|
|
379
|
-
fields=
|
|
380
|
-
custom_fields=
|
|
374
|
+
fields=fields,
|
|
375
|
+
custom_fields=customs,
|
|
381
376
|
)
|
|
382
|
-
|
|
383
|
-
@classmethod
|
|
384
|
-
async def get_object(cls, request: HttpRequest, pk: int | str):
|
|
385
|
-
q = {cls._meta.pk.attname: pk}
|
|
386
|
-
try:
|
|
387
|
-
obj = (
|
|
388
|
-
await (await cls.queryset_request(request))
|
|
389
|
-
.prefetch_related(*cls.get_reverse_relations())
|
|
390
|
-
.aget(**q)
|
|
391
|
-
)
|
|
392
|
-
except ObjectDoesNotExist:
|
|
393
|
-
raise SerializeError({cls._meta.model_name: "not found"}, 404)
|
|
394
|
-
return obj
|
|
395
|
-
|
|
396
|
-
@classmethod
|
|
397
|
-
async def create_s(cls, request: HttpRequest, data: Schema):
|
|
398
|
-
try:
|
|
399
|
-
payload, customs = await cls.parse_input_data(request, data)
|
|
400
|
-
pk = (await cls.objects.acreate(**payload)).pk
|
|
401
|
-
obj = await cls.get_object(request, pk)
|
|
402
|
-
except SerializeError as e:
|
|
403
|
-
return e.status_code, e.error
|
|
404
|
-
payload |= customs
|
|
405
|
-
await obj.custom_actions(payload)
|
|
406
|
-
await obj.post_create()
|
|
407
|
-
return await cls.read_s(request, obj)
|
|
408
|
-
|
|
409
|
-
@classmethod
|
|
410
|
-
async def read_s(cls, request: HttpRequest, obj: type["ModelSerializer"]):
|
|
411
|
-
schema = cls.generate_read_s().from_orm(obj)
|
|
412
|
-
try:
|
|
413
|
-
data = await cls.parse_output_data(request, schema)
|
|
414
|
-
except SerializeError as e:
|
|
415
|
-
return e.status_code, e.error
|
|
416
|
-
return data
|
|
417
|
-
|
|
418
|
-
@classmethod
|
|
419
|
-
async def update_s(cls, request: HttpRequest, data: Schema, pk: int | str):
|
|
420
|
-
try:
|
|
421
|
-
obj = await cls.get_object(request, pk)
|
|
422
|
-
except SerializeError as e:
|
|
423
|
-
return e.status_code, e.error
|
|
424
|
-
|
|
425
|
-
payload, customs = await cls.parse_input_data(request, data)
|
|
426
|
-
for k, v in payload.items():
|
|
427
|
-
if v is not None:
|
|
428
|
-
setattr(obj, k, v)
|
|
429
|
-
await obj.custom_actions(customs)
|
|
430
|
-
await obj.asave()
|
|
431
|
-
updated_object = await cls.get_object(request, pk)
|
|
432
|
-
return await cls.read_s(request, updated_object)
|
|
433
|
-
|
|
434
|
-
@classmethod
|
|
435
|
-
async def delete_s(cls, request: HttpRequest, pk: int | str):
|
|
436
|
-
try:
|
|
437
|
-
obj = await cls.get_object(request, pk)
|
|
438
|
-
except SerializeError as e:
|
|
439
|
-
return e.status_code, e.error
|
|
440
|
-
await obj.adelete()
|
|
441
|
-
return HttpResponse(status=204)
|
ninja_aio/types.py
CHANGED
ninja_aio/views.py
CHANGED
|
@@ -8,7 +8,6 @@ from django.db.models import Model
|
|
|
8
8
|
|
|
9
9
|
from .models import ModelSerializer, ModelUtil
|
|
10
10
|
from .schemas import GenericMessageSchema
|
|
11
|
-
from .exceptions import SerializeError
|
|
12
11
|
from .types import ModelSerializerMeta
|
|
13
12
|
|
|
14
13
|
ERROR_CODES = frozenset({400, 401, 404, 428})
|
|
@@ -136,10 +135,7 @@ class APIViewSet:
|
|
|
136
135
|
response={200: self.schema_out, self.error_codes: GenericMessageSchema},
|
|
137
136
|
)
|
|
138
137
|
async def retrieve(request: HttpRequest, pk: int | str):
|
|
139
|
-
|
|
140
|
-
obj = await self.model_util.get_object(request, pk)
|
|
141
|
-
except SerializeError as e:
|
|
142
|
-
return e.status_code, e.error
|
|
138
|
+
obj = await self.model_util.get_object(request, pk)
|
|
143
139
|
return await self.model_util.read_s(request, obj, self.schema_out)
|
|
144
140
|
|
|
145
141
|
retrieve.__name__ = f"retrieve_{self.model._meta.model_name}"
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
ninja_aio/__init__.py,sha256=_tjey_xwfvsIhCY5_zwh_pEW7Iubpix0RnfUSx_x9to,120
|
|
2
|
-
ninja_aio/api.py,sha256=r0BOsZLdH-kqInbbXPmK8HeF-fQz756ih-MSFvfEBXI,1546
|
|
3
|
-
ninja_aio/auth.py,sha256=hGgiblvffpHmmakjaxdNT3G0tq39-Bvc5oLNqjn4qd8,1300
|
|
4
|
-
ninja_aio/exceptions.py,sha256=PPNr1CdC7M7Kx1MtiBGuR4hATYQqEuvxCRU6uSG7rgM,543
|
|
5
|
-
ninja_aio/models.py,sha256=Ni4nO9U2DeWA3STX2wV8Jk5esqgwOSzfW7ZvA4hu1Hg,16500
|
|
6
|
-
ninja_aio/parsers.py,sha256=e_4lGCPV7zs-HTqtdJTc8yQD2KPAn9njbL8nF_Mmgkc,153
|
|
7
|
-
ninja_aio/renders.py,sha256=mHeKNJtmDhZmgFpS9B6SPn5uZFcyVXrsoMhr149LeW8,1555
|
|
8
|
-
ninja_aio/schemas.py,sha256=EgRkfhnzZqwGvdBmqlZixMtMcoD1ZxV_qzJ3fmaAy20,113
|
|
9
|
-
ninja_aio/types.py,sha256=gEHb6O9M07o5DHtxXH_KAcFJdq9lzq3ljmYbNLst8vs,297
|
|
10
|
-
ninja_aio/views.py,sha256=LcUoCyBs4Sbgz8ugcu9nfm30xCKnEnEalROg-mUOCTg,7117
|
|
11
|
-
django_ninja_aio_crud-0.3.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
12
|
-
django_ninja_aio_crud-0.3.1.dist-info/METADATA,sha256=c6tJqkqsYu7iUZdP5X5Rbm2jsGkeZOMAVGlDxtEJCOI,10135
|
|
13
|
-
django_ninja_aio_crud-0.3.1.dist-info/RECORD,,
|
|
File without changes
|