django-ninja-aio-crud 0.10.2__py3-none-any.whl → 2.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-2.4.0.dist-info/METADATA +382 -0
- django_ninja_aio_crud-2.4.0.dist-info/RECORD +29 -0
- ninja_aio/__init__.py +1 -1
- ninja_aio/api.py +24 -2
- ninja_aio/auth.py +186 -4
- ninja_aio/decorators/__init__.py +23 -0
- ninja_aio/decorators/operations.py +9 -0
- ninja_aio/decorators/views.py +219 -0
- ninja_aio/exceptions.py +36 -1
- ninja_aio/factory/__init__.py +3 -0
- ninja_aio/factory/operations.py +296 -0
- ninja_aio/helpers/__init__.py +0 -0
- ninja_aio/helpers/api.py +506 -0
- ninja_aio/helpers/query.py +108 -0
- ninja_aio/models/__init__.py +4 -0
- ninja_aio/models/serializers.py +738 -0
- ninja_aio/models/utils.py +894 -0
- ninja_aio/renders.py +26 -26
- ninja_aio/schemas/__init__.py +23 -0
- ninja_aio/{schemas.py → schemas/api.py} +0 -5
- ninja_aio/schemas/generics.py +5 -0
- ninja_aio/schemas/helpers.py +170 -0
- ninja_aio/types.py +3 -1
- ninja_aio/views/__init__.py +3 -0
- ninja_aio/views/api.py +582 -0
- ninja_aio/views/mixins.py +275 -0
- django_ninja_aio_crud-0.10.2.dist-info/METADATA +0 -526
- django_ninja_aio_crud-0.10.2.dist-info/RECORD +0 -14
- ninja_aio/models.py +0 -549
- ninja_aio/views.py +0 -522
- {django_ninja_aio_crud-0.10.2.dist-info → django_ninja_aio_crud-2.4.0.dist-info}/WHEEL +0 -0
- {django_ninja_aio_crud-0.10.2.dist-info → django_ninja_aio_crud-2.4.0.dist-info}/licenses/LICENSE +0 -0
ninja_aio/models.py
DELETED
|
@@ -1,549 +0,0 @@
|
|
|
1
|
-
import asyncio
|
|
2
|
-
import base64
|
|
3
|
-
from typing import Any
|
|
4
|
-
|
|
5
|
-
from ninja import Schema
|
|
6
|
-
from ninja.orm import create_schema
|
|
7
|
-
|
|
8
|
-
from django.db import models
|
|
9
|
-
from django.http import HttpRequest
|
|
10
|
-
from django.core.exceptions import ObjectDoesNotExist
|
|
11
|
-
from asgiref.sync import sync_to_async
|
|
12
|
-
from django.db.models.fields.related_descriptors import (
|
|
13
|
-
ReverseManyToOneDescriptor,
|
|
14
|
-
ReverseOneToOneDescriptor,
|
|
15
|
-
ManyToManyDescriptor,
|
|
16
|
-
ForwardManyToOneDescriptor,
|
|
17
|
-
ForwardOneToOneDescriptor,
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
from .exceptions import SerializeError
|
|
21
|
-
from .types import S_TYPES, F_TYPES, SCHEMA_TYPES, ModelSerializerMeta
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
async def agetattr(obj, name: str, default=None):
|
|
25
|
-
return await sync_to_async(getattr)(obj, name, default)
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
class ModelUtil:
|
|
29
|
-
def __init__(self, model: type["ModelSerializer"] | models.Model):
|
|
30
|
-
self.model = model
|
|
31
|
-
|
|
32
|
-
@property
|
|
33
|
-
def serializable_fields(self):
|
|
34
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
35
|
-
return self.model.get_fields("read")
|
|
36
|
-
return [field.name for field in self.model._meta.get_fields()]
|
|
37
|
-
|
|
38
|
-
@property
|
|
39
|
-
def model_name(self) -> str:
|
|
40
|
-
return self.model._meta.model_name
|
|
41
|
-
|
|
42
|
-
@property
|
|
43
|
-
def model_pk_name(self) -> str:
|
|
44
|
-
return self.model._meta.pk.attname
|
|
45
|
-
|
|
46
|
-
@property
|
|
47
|
-
def model_verbose_name_plural(self) -> str:
|
|
48
|
-
return self.model._meta.verbose_name_plural
|
|
49
|
-
|
|
50
|
-
def verbose_name_path_resolver(self) -> str:
|
|
51
|
-
return "-".join(self.model_verbose_name_plural.split(" "))
|
|
52
|
-
|
|
53
|
-
def verbose_name_view_resolver(self) -> str:
|
|
54
|
-
return self.model_verbose_name_plural.replace(" ", "")
|
|
55
|
-
|
|
56
|
-
async def get_object(
|
|
57
|
-
self,
|
|
58
|
-
request: HttpRequest,
|
|
59
|
-
pk: int | str = None,
|
|
60
|
-
filters: dict = None,
|
|
61
|
-
getters: dict = None,
|
|
62
|
-
with_qs_request=True,
|
|
63
|
-
) -> (
|
|
64
|
-
type["ModelSerializer"]
|
|
65
|
-
| models.Model
|
|
66
|
-
| models.QuerySet[type["ModelSerializer"] | models.Model]
|
|
67
|
-
):
|
|
68
|
-
get_q = {self.model_pk_name: pk} if pk is not None else {}
|
|
69
|
-
if getters:
|
|
70
|
-
get_q |= getters
|
|
71
|
-
|
|
72
|
-
obj_qs = self.model.objects.select_related()
|
|
73
|
-
if isinstance(self.model, ModelSerializerMeta) and with_qs_request:
|
|
74
|
-
obj_qs = await self.model.queryset_request(request)
|
|
75
|
-
|
|
76
|
-
obj_qs = obj_qs.prefetch_related(*self.get_reverse_relations())
|
|
77
|
-
if filters:
|
|
78
|
-
obj_qs = obj_qs.filter(**filters)
|
|
79
|
-
|
|
80
|
-
if not get_q:
|
|
81
|
-
return obj_qs
|
|
82
|
-
|
|
83
|
-
try:
|
|
84
|
-
obj = await obj_qs.aget(**get_q)
|
|
85
|
-
except ObjectDoesNotExist:
|
|
86
|
-
raise SerializeError({self.model_name: "not found"}, 404)
|
|
87
|
-
|
|
88
|
-
return obj
|
|
89
|
-
|
|
90
|
-
def get_reverse_relations(self) -> list[str]:
|
|
91
|
-
reverse_rels = []
|
|
92
|
-
for f in self.serializable_fields:
|
|
93
|
-
field_obj = getattr(self.model, f)
|
|
94
|
-
if isinstance(field_obj, ManyToManyDescriptor):
|
|
95
|
-
reverse_rels.append(f)
|
|
96
|
-
continue
|
|
97
|
-
if isinstance(field_obj, ReverseManyToOneDescriptor):
|
|
98
|
-
reverse_rels.append(field_obj.field._related_name)
|
|
99
|
-
continue
|
|
100
|
-
if isinstance(field_obj, ReverseOneToOneDescriptor):
|
|
101
|
-
reverse_rels.append(field_obj.related.name)
|
|
102
|
-
return reverse_rels
|
|
103
|
-
|
|
104
|
-
async def parse_input_data(self, request: HttpRequest, data: Schema):
|
|
105
|
-
payload = data.model_dump(mode="json")
|
|
106
|
-
customs = {}
|
|
107
|
-
optionals = []
|
|
108
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
109
|
-
customs = {k: v for k, v in payload.items() if self.model.is_custom(k)}
|
|
110
|
-
optionals = [
|
|
111
|
-
k for k, v in payload.items() if self.model.is_optional(k) and v is None
|
|
112
|
-
]
|
|
113
|
-
for k, v in payload.items():
|
|
114
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
115
|
-
if self.model.is_custom(k):
|
|
116
|
-
continue
|
|
117
|
-
if self.model.is_optional(k) and v is None:
|
|
118
|
-
continue
|
|
119
|
-
field_obj = (await agetattr(self.model, k)).field
|
|
120
|
-
if isinstance(field_obj, models.BinaryField):
|
|
121
|
-
try:
|
|
122
|
-
payload |= {k: base64.b64decode(v)}
|
|
123
|
-
except Exception as exc:
|
|
124
|
-
raise SerializeError({k: ". ".join(exc.args)}, 400)
|
|
125
|
-
if isinstance(field_obj, models.ForeignKey):
|
|
126
|
-
rel_util = ModelUtil(field_obj.related_model)
|
|
127
|
-
rel: ModelSerializer = await rel_util.get_object(
|
|
128
|
-
request, v, with_qs_request=False
|
|
129
|
-
)
|
|
130
|
-
payload |= {k: rel}
|
|
131
|
-
new_payload = {
|
|
132
|
-
k: v for k, v in payload.items() if k not in (customs.keys() or optionals)
|
|
133
|
-
}
|
|
134
|
-
return new_payload, customs
|
|
135
|
-
|
|
136
|
-
async def parse_output_data(self, request: HttpRequest, data: Schema):
|
|
137
|
-
olds_k: list[dict] = []
|
|
138
|
-
payload = data.model_dump(mode="json")
|
|
139
|
-
for k, v in payload.items():
|
|
140
|
-
try:
|
|
141
|
-
field_obj = (await agetattr(self.model, k)).field
|
|
142
|
-
except AttributeError:
|
|
143
|
-
try:
|
|
144
|
-
field_obj = (await agetattr(self.model, k)).related
|
|
145
|
-
except AttributeError:
|
|
146
|
-
pass
|
|
147
|
-
if isinstance(v, dict) and (
|
|
148
|
-
isinstance(field_obj, models.ForeignKey)
|
|
149
|
-
or isinstance(field_obj, models.OneToOneField)
|
|
150
|
-
):
|
|
151
|
-
rel_util = ModelUtil(field_obj.related_model)
|
|
152
|
-
rel: ModelSerializer = await rel_util.get_object(
|
|
153
|
-
request, v.get(rel_util.model_pk_name)
|
|
154
|
-
)
|
|
155
|
-
if isinstance(field_obj, models.ForeignKey):
|
|
156
|
-
for rel_k, rel_v in v.items():
|
|
157
|
-
field_rel_obj = await agetattr(rel, rel_k)
|
|
158
|
-
if isinstance(field_rel_obj, models.ForeignKey):
|
|
159
|
-
olds_k.append({rel_k: rel_v})
|
|
160
|
-
for obj in olds_k:
|
|
161
|
-
for old_k, old_v in obj.items():
|
|
162
|
-
v.pop(old_k)
|
|
163
|
-
v |= {f"{old_k}_id": old_v}
|
|
164
|
-
olds_k = []
|
|
165
|
-
payload |= {k: rel}
|
|
166
|
-
return payload
|
|
167
|
-
|
|
168
|
-
async def create_s(self, request: HttpRequest, data: Schema, obj_schema: Schema):
|
|
169
|
-
payload, customs = await self.parse_input_data(request, data)
|
|
170
|
-
pk = (await self.model.objects.acreate(**payload)).pk
|
|
171
|
-
obj = await self.get_object(request, pk)
|
|
172
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
173
|
-
await asyncio.gather(obj.custom_actions(customs), obj.post_create())
|
|
174
|
-
return await self.read_s(request, obj, obj_schema)
|
|
175
|
-
|
|
176
|
-
async def read_s(
|
|
177
|
-
self,
|
|
178
|
-
request: HttpRequest,
|
|
179
|
-
obj: type["ModelSerializer"],
|
|
180
|
-
obj_schema: Schema,
|
|
181
|
-
):
|
|
182
|
-
if obj_schema is None:
|
|
183
|
-
raise SerializeError({"obj_schema": "must be provided"}, 400)
|
|
184
|
-
return await self.parse_output_data(
|
|
185
|
-
request, await sync_to_async(obj_schema.from_orm)(obj)
|
|
186
|
-
)
|
|
187
|
-
|
|
188
|
-
async def update_s(
|
|
189
|
-
self, request: HttpRequest, data: Schema, pk: int | str, obj_schema: Schema
|
|
190
|
-
):
|
|
191
|
-
obj = await self.get_object(request, pk)
|
|
192
|
-
payload, customs = await self.parse_input_data(request, data)
|
|
193
|
-
for k, v in payload.items():
|
|
194
|
-
if v is not None:
|
|
195
|
-
setattr(obj, k, v)
|
|
196
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
197
|
-
await obj.custom_actions(customs)
|
|
198
|
-
await obj.asave()
|
|
199
|
-
updated_object = await self.get_object(request, pk)
|
|
200
|
-
return await self.read_s(request, updated_object, obj_schema)
|
|
201
|
-
|
|
202
|
-
async def delete_s(self, request: HttpRequest, pk: int | str):
|
|
203
|
-
obj = await self.get_object(request, pk)
|
|
204
|
-
await obj.adelete()
|
|
205
|
-
return None
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
209
|
-
class Meta:
|
|
210
|
-
abstract = True
|
|
211
|
-
|
|
212
|
-
class CreateSerializer:
|
|
213
|
-
fields: list[str] = []
|
|
214
|
-
customs: list[tuple[str, type, Any]] = []
|
|
215
|
-
optionals: list[tuple[str, type]] = []
|
|
216
|
-
excludes: list[str] = []
|
|
217
|
-
|
|
218
|
-
class ReadSerializer:
|
|
219
|
-
fields: list[str] = []
|
|
220
|
-
excludes: list[str] = []
|
|
221
|
-
customs: list[tuple[str, type, Any]] = []
|
|
222
|
-
|
|
223
|
-
class UpdateSerializer:
|
|
224
|
-
fields: list[str] = []
|
|
225
|
-
customs: list[tuple[str, type, Any]] = []
|
|
226
|
-
optionals: list[tuple[str, type]] = []
|
|
227
|
-
excludes: list[str] = []
|
|
228
|
-
|
|
229
|
-
@property
|
|
230
|
-
def has_custom_fields_create(self):
|
|
231
|
-
return hasattr(self.CreateSerializer, "customs")
|
|
232
|
-
|
|
233
|
-
@property
|
|
234
|
-
def has_custom_fields_update(self):
|
|
235
|
-
return hasattr(self.UpdateSerializer, "customs")
|
|
236
|
-
|
|
237
|
-
@property
|
|
238
|
-
def has_custom_fields(self):
|
|
239
|
-
return self.has_custom_fields_create or self.has_custom_fields_update
|
|
240
|
-
|
|
241
|
-
@property
|
|
242
|
-
def has_optional_fields_create(self):
|
|
243
|
-
return hasattr(self.CreateSerializer, "optionals")
|
|
244
|
-
|
|
245
|
-
@property
|
|
246
|
-
def has_optional_fields_update(self):
|
|
247
|
-
return hasattr(self.UpdateSerializer, "optionals")
|
|
248
|
-
|
|
249
|
-
@property
|
|
250
|
-
def has_optional_fields(self):
|
|
251
|
-
return self.has_optional_fields_create or self.has_optional_fields_update
|
|
252
|
-
|
|
253
|
-
@classmethod
|
|
254
|
-
def _get_fields(cls, s_type: type[S_TYPES], f_type: type[F_TYPES]):
|
|
255
|
-
match s_type:
|
|
256
|
-
case "create":
|
|
257
|
-
fields = getattr(cls.CreateSerializer, f_type, [])
|
|
258
|
-
case "update":
|
|
259
|
-
fields = getattr(cls.UpdateSerializer, f_type, [])
|
|
260
|
-
case "read":
|
|
261
|
-
fields = getattr(cls.ReadSerializer, f_type, [])
|
|
262
|
-
return fields
|
|
263
|
-
|
|
264
|
-
@classmethod
|
|
265
|
-
def _is_special_field(
|
|
266
|
-
cls, s_type: type[S_TYPES], field: str, f_type: type[F_TYPES]
|
|
267
|
-
):
|
|
268
|
-
special_fields = cls._get_fields(s_type, f_type)
|
|
269
|
-
return any(field in special_f for special_f in special_fields)
|
|
270
|
-
|
|
271
|
-
@classmethod
|
|
272
|
-
def _generate_model_schema(
|
|
273
|
-
cls,
|
|
274
|
-
schema_type: type[SCHEMA_TYPES],
|
|
275
|
-
depth: int = None,
|
|
276
|
-
) -> Schema:
|
|
277
|
-
match schema_type:
|
|
278
|
-
case "In":
|
|
279
|
-
s_type = "create"
|
|
280
|
-
case "Patch":
|
|
281
|
-
s_type = "update"
|
|
282
|
-
case "Out":
|
|
283
|
-
fields, reverse_rels, excludes, customs = cls.get_schema_out_data()
|
|
284
|
-
if not fields and not reverse_rels and not excludes and not customs:
|
|
285
|
-
return None
|
|
286
|
-
return create_schema(
|
|
287
|
-
model=cls,
|
|
288
|
-
name=f"{cls._meta.model_name}SchemaOut",
|
|
289
|
-
depth=depth,
|
|
290
|
-
fields=fields,
|
|
291
|
-
custom_fields=reverse_rels + customs,
|
|
292
|
-
exclude=excludes,
|
|
293
|
-
)
|
|
294
|
-
case "Related":
|
|
295
|
-
fields, customs = cls.get_related_schema_data()
|
|
296
|
-
if not fields and not customs:
|
|
297
|
-
return None
|
|
298
|
-
return create_schema(
|
|
299
|
-
model=cls,
|
|
300
|
-
name=f"{cls._meta.model_name}SchemaRelated",
|
|
301
|
-
fields=fields,
|
|
302
|
-
custom_fields=customs,
|
|
303
|
-
)
|
|
304
|
-
|
|
305
|
-
fields = cls.get_fields(s_type)
|
|
306
|
-
optionals = cls.get_optional_fields(s_type)
|
|
307
|
-
customs = cls.get_custom_fields(s_type) + optionals
|
|
308
|
-
excludes = cls.get_excluded_fields(s_type)
|
|
309
|
-
if not fields and not excludes:
|
|
310
|
-
fields = [f[0] for f in optionals]
|
|
311
|
-
return (
|
|
312
|
-
create_schema(
|
|
313
|
-
model=cls,
|
|
314
|
-
name=f"{cls._meta.model_name}Schema{schema_type}",
|
|
315
|
-
fields=fields,
|
|
316
|
-
custom_fields=customs,
|
|
317
|
-
exclude=excludes,
|
|
318
|
-
)
|
|
319
|
-
if fields or customs or excludes
|
|
320
|
-
else None
|
|
321
|
-
)
|
|
322
|
-
|
|
323
|
-
@classmethod
|
|
324
|
-
def verbose_name_path_resolver(cls) -> str:
|
|
325
|
-
return "-".join(cls._meta.verbose_name_plural.split(" "))
|
|
326
|
-
|
|
327
|
-
def has_changed(self, field: str) -> bool:
|
|
328
|
-
"""
|
|
329
|
-
Check if a model field has changed
|
|
330
|
-
"""
|
|
331
|
-
if not self.pk:
|
|
332
|
-
return False
|
|
333
|
-
old_value = (
|
|
334
|
-
self.__class__._default_manager.filter(pk=self.pk)
|
|
335
|
-
.values(field)
|
|
336
|
-
.get()[field]
|
|
337
|
-
)
|
|
338
|
-
return getattr(self, field) != old_value
|
|
339
|
-
|
|
340
|
-
@classmethod
|
|
341
|
-
async def queryset_request(cls, request: HttpRequest):
|
|
342
|
-
"""
|
|
343
|
-
Override this method to return a filtered queryset based
|
|
344
|
-
on the request received
|
|
345
|
-
"""
|
|
346
|
-
return cls.objects.select_related().all()
|
|
347
|
-
|
|
348
|
-
async def post_create(self) -> None:
|
|
349
|
-
"""
|
|
350
|
-
Override this method to execute code after the object
|
|
351
|
-
has been created
|
|
352
|
-
"""
|
|
353
|
-
pass
|
|
354
|
-
|
|
355
|
-
async def custom_actions(self, payload: dict[str, Any]):
|
|
356
|
-
"""
|
|
357
|
-
Override this method to execute custom actions based on
|
|
358
|
-
custom given fields. It could be useful for post create method.
|
|
359
|
-
"""
|
|
360
|
-
pass
|
|
361
|
-
|
|
362
|
-
@classmethod
|
|
363
|
-
def get_related_schema_data(cls):
|
|
364
|
-
fields = cls.get_fields("read")
|
|
365
|
-
custom_f = {
|
|
366
|
-
name: (value, default)
|
|
367
|
-
for name, value, default in cls.get_custom_fields("read")
|
|
368
|
-
}
|
|
369
|
-
_related_fields = []
|
|
370
|
-
for f in fields + list(custom_f.keys()):
|
|
371
|
-
field_obj = getattr(cls, f)
|
|
372
|
-
if not isinstance(
|
|
373
|
-
field_obj,
|
|
374
|
-
(
|
|
375
|
-
ManyToManyDescriptor,
|
|
376
|
-
ReverseManyToOneDescriptor,
|
|
377
|
-
ReverseOneToOneDescriptor,
|
|
378
|
-
ForwardManyToOneDescriptor,
|
|
379
|
-
ForwardOneToOneDescriptor,
|
|
380
|
-
),
|
|
381
|
-
):
|
|
382
|
-
_related_fields.append(f)
|
|
383
|
-
|
|
384
|
-
if not _related_fields:
|
|
385
|
-
return None, None
|
|
386
|
-
|
|
387
|
-
custom_related_fields = [
|
|
388
|
-
(f, *custom_f[f]) for f in _related_fields if f in custom_f
|
|
389
|
-
]
|
|
390
|
-
related_fields = [f for f in _related_fields if f not in custom_f]
|
|
391
|
-
return related_fields, custom_related_fields
|
|
392
|
-
|
|
393
|
-
@classmethod
|
|
394
|
-
def get_schema_out_data(cls):
|
|
395
|
-
fields = []
|
|
396
|
-
reverse_rels = []
|
|
397
|
-
rels = []
|
|
398
|
-
for f in cls.get_fields("read"):
|
|
399
|
-
field_obj = getattr(cls, f)
|
|
400
|
-
if isinstance(
|
|
401
|
-
field_obj,
|
|
402
|
-
(
|
|
403
|
-
ManyToManyDescriptor,
|
|
404
|
-
ReverseManyToOneDescriptor,
|
|
405
|
-
ReverseOneToOneDescriptor,
|
|
406
|
-
),
|
|
407
|
-
):
|
|
408
|
-
if isinstance(field_obj, ManyToManyDescriptor):
|
|
409
|
-
rel_obj: ModelSerializer = field_obj.field.related_model
|
|
410
|
-
if field_obj.reverse:
|
|
411
|
-
rel_obj = field_obj.field.model
|
|
412
|
-
rel_type = "many"
|
|
413
|
-
elif isinstance(field_obj, ReverseManyToOneDescriptor):
|
|
414
|
-
rel_obj = field_obj.field.model
|
|
415
|
-
rel_type = "many"
|
|
416
|
-
else: # ReverseOneToOneDescriptor
|
|
417
|
-
rel_obj = field_obj.related.related_model
|
|
418
|
-
rel_type = "one"
|
|
419
|
-
if not isinstance(rel_obj, ModelSerializerMeta):
|
|
420
|
-
continue
|
|
421
|
-
if not rel_obj.get_fields("read") and not rel_obj.get_custom_fields(
|
|
422
|
-
"read"
|
|
423
|
-
):
|
|
424
|
-
continue
|
|
425
|
-
rel_schema = (
|
|
426
|
-
rel_obj.generate_related_s()
|
|
427
|
-
if rel_type != "many"
|
|
428
|
-
else list[rel_obj.generate_related_s()]
|
|
429
|
-
)
|
|
430
|
-
rel_data = (f, rel_schema | None, None)
|
|
431
|
-
reverse_rels.append(rel_data)
|
|
432
|
-
continue
|
|
433
|
-
if isinstance(
|
|
434
|
-
field_obj, (ForwardOneToOneDescriptor, ForwardManyToOneDescriptor)
|
|
435
|
-
):
|
|
436
|
-
rel_obj = field_obj.field.related_model
|
|
437
|
-
if not isinstance(rel_obj, ModelSerializerMeta):
|
|
438
|
-
fields.append(f)
|
|
439
|
-
continue
|
|
440
|
-
if not rel_obj.get_fields("read") and not rel_obj.get_custom_fields(
|
|
441
|
-
"read"
|
|
442
|
-
):
|
|
443
|
-
continue
|
|
444
|
-
rel_data = (f, rel_obj.generate_related_s() | None, None)
|
|
445
|
-
rels.append(rel_data)
|
|
446
|
-
continue
|
|
447
|
-
fields.append(f)
|
|
448
|
-
return (
|
|
449
|
-
fields,
|
|
450
|
-
reverse_rels,
|
|
451
|
-
cls.get_excluded_fields("read"),
|
|
452
|
-
cls.get_custom_fields("read") + rels,
|
|
453
|
-
)
|
|
454
|
-
|
|
455
|
-
@classmethod
|
|
456
|
-
def is_custom(cls, field: str):
|
|
457
|
-
return cls._is_special_field(
|
|
458
|
-
"create", field, "customs"
|
|
459
|
-
) or cls._is_special_field("update", field, "customs")
|
|
460
|
-
|
|
461
|
-
@classmethod
|
|
462
|
-
def is_optional(cls, field: str):
|
|
463
|
-
return cls._is_special_field(
|
|
464
|
-
"create", field, "optionals"
|
|
465
|
-
) or cls._is_special_field("update", field, "optionals")
|
|
466
|
-
|
|
467
|
-
@classmethod
|
|
468
|
-
def get_custom_fields(cls, s_type: type[S_TYPES]) -> list[tuple]:
|
|
469
|
-
return cls._get_fields(s_type, "customs")
|
|
470
|
-
|
|
471
|
-
@classmethod
|
|
472
|
-
def get_optional_fields(cls, s_type: type[S_TYPES]):
|
|
473
|
-
return [
|
|
474
|
-
(field, field_type, None)
|
|
475
|
-
for field, field_type in cls._get_fields(s_type, "optionals")
|
|
476
|
-
]
|
|
477
|
-
|
|
478
|
-
@classmethod
|
|
479
|
-
def get_excluded_fields(cls, s_type: type[S_TYPES]):
|
|
480
|
-
return cls._get_fields(s_type, "excludes")
|
|
481
|
-
|
|
482
|
-
@classmethod
|
|
483
|
-
def get_fields(cls, s_type: type[S_TYPES]):
|
|
484
|
-
return cls._get_fields(s_type, "fields")
|
|
485
|
-
|
|
486
|
-
@classmethod
|
|
487
|
-
def generate_read_s(cls, depth: int = 1) -> Schema:
|
|
488
|
-
return cls._generate_model_schema("Out", depth)
|
|
489
|
-
|
|
490
|
-
@classmethod
|
|
491
|
-
def generate_create_s(cls) -> Schema:
|
|
492
|
-
return cls._generate_model_schema("In")
|
|
493
|
-
|
|
494
|
-
@classmethod
|
|
495
|
-
def generate_update_s(cls) -> Schema:
|
|
496
|
-
return cls._generate_model_schema("Patch")
|
|
497
|
-
|
|
498
|
-
@classmethod
|
|
499
|
-
def generate_related_s(cls) -> Schema:
|
|
500
|
-
return cls._generate_model_schema("Related")
|
|
501
|
-
|
|
502
|
-
def after_save(self):
|
|
503
|
-
"""
|
|
504
|
-
Override this method to execute code after the object
|
|
505
|
-
has been saved
|
|
506
|
-
"""
|
|
507
|
-
pass
|
|
508
|
-
|
|
509
|
-
def before_save(self):
|
|
510
|
-
"""
|
|
511
|
-
Override this method to execute code before the object
|
|
512
|
-
has been saved
|
|
513
|
-
"""
|
|
514
|
-
pass
|
|
515
|
-
|
|
516
|
-
def on_create_after_save(self):
|
|
517
|
-
"""
|
|
518
|
-
Override this method to execute code after the object
|
|
519
|
-
has been created
|
|
520
|
-
"""
|
|
521
|
-
pass
|
|
522
|
-
|
|
523
|
-
def on_create_before_save(self):
|
|
524
|
-
"""
|
|
525
|
-
Override this method to execute code before the object
|
|
526
|
-
has been created
|
|
527
|
-
"""
|
|
528
|
-
pass
|
|
529
|
-
|
|
530
|
-
def on_delete(self):
|
|
531
|
-
"""
|
|
532
|
-
Override this method to execute code after the object
|
|
533
|
-
has been deleted
|
|
534
|
-
"""
|
|
535
|
-
pass
|
|
536
|
-
|
|
537
|
-
def save(self, *args, **kwargs):
|
|
538
|
-
if self._state.adding:
|
|
539
|
-
self.on_create_before_save()
|
|
540
|
-
self.before_save()
|
|
541
|
-
super().save(*args, **kwargs)
|
|
542
|
-
if self._state.adding:
|
|
543
|
-
self.on_create_after_save()
|
|
544
|
-
self.after_save()
|
|
545
|
-
|
|
546
|
-
def delete(self, *args, **kwargs):
|
|
547
|
-
res = super().delete(*args, **kwargs)
|
|
548
|
-
self.on_delete()
|
|
549
|
-
return res
|