django-ninja-aio-crud 0.6.1__py3-none-any.whl → 0.6.3__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.6.1.dist-info → django_ninja_aio_crud-0.6.3.dist-info}/METADATA +31 -2
- django_ninja_aio_crud-0.6.3.dist-info/RECORD +12 -0
- ninja_aio/__init__.py +1 -1
- ninja_aio/auth.py +3 -13
- ninja_aio/exceptions.py +8 -0
- ninja_aio/models.py +41 -23
- django_ninja_aio_crud-0.6.1.dist-info/RECORD +0 -13
- ninja_aio/views.py +0 -271
- {django_ninja_aio_crud-0.6.1.dist-info → django_ninja_aio_crud-0.6.3.dist-info}/WHEEL +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: django-ninja-aio-crud
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Django Ninja AIO CRUD - Rest Framework
|
|
5
5
|
Author: Giuseppe Casillo
|
|
6
6
|
Requires-Python: >=3.10
|
|
@@ -100,7 +100,7 @@ class Foo(ModelSerializer):
|
|
|
100
100
|
fields = ["name", "bar"]
|
|
101
101
|
```
|
|
102
102
|
|
|
103
|
-
- 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
|
|
103
|
+
- 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.
|
|
104
104
|
|
|
105
105
|
```python
|
|
106
106
|
# models.py
|
|
@@ -184,6 +184,35 @@ class Foo(ModelSerializer):
|
|
|
184
184
|
excludes = ["id", "name"]
|
|
185
185
|
optionals = [("bar", str), ("active", bool)]
|
|
186
186
|
```
|
|
187
|
+
- If you want to add into ReadSerializer model properties you must define them as customs.
|
|
188
|
+
```python
|
|
189
|
+
# models.py
|
|
190
|
+
from django.db import models
|
|
191
|
+
from ninja_aio.models import ModelSerializer
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class Foo(ModelSerializer):
|
|
195
|
+
name = models.CharField(max_length=30)
|
|
196
|
+
bar = models.CharField(max_length=30, default="")
|
|
197
|
+
active = models.BooleanField(default=False)
|
|
198
|
+
|
|
199
|
+
@property
|
|
200
|
+
def full_name(self):
|
|
201
|
+
return f"{self.name} example_full_name"
|
|
202
|
+
|
|
203
|
+
class ReadSerializer:
|
|
204
|
+
excludes = ["bar"]
|
|
205
|
+
customs = [("full_name", str, "")]
|
|
206
|
+
|
|
207
|
+
class CreateSerializer:
|
|
208
|
+
fields = ["name"]
|
|
209
|
+
optionals = [("bar", str), ("active", bool)]
|
|
210
|
+
|
|
211
|
+
class UpdateSerializer:
|
|
212
|
+
excludes = ["id", "name"]
|
|
213
|
+
optionals = [("bar", str), ("active", bool)]
|
|
214
|
+
```
|
|
215
|
+
|
|
187
216
|
|
|
188
217
|
|
|
189
218
|
### APIViewSet
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
ninja_aio/__init__.py,sha256=ZONk9sZ6Zj3LZKMblnnSP8auyeIk3DqIEMqQnMuIX60,119
|
|
2
|
+
ninja_aio/api.py,sha256=Fe6l3YCy7MW5TY4-Lbl80CFuK2NT2Y7tHfmqPk6Mqak,1735
|
|
3
|
+
ninja_aio/auth.py,sha256=fKboioU4sezPukKJukIwiboxml_KV7irhCH3vGYt5pU,1008
|
|
4
|
+
ninja_aio/exceptions.py,sha256=gPnZX1Do2GXudbU8wDYkwhO70Qj0ZNrIJJ2UXRs9vYk,2241
|
|
5
|
+
ninja_aio/models.py,sha256=p8X_G2WVlygDE8UEJ7O2fajauJYu1d_pgcvFPDkHRwc,15638
|
|
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=EHznS-6KWLwSX5hLeXbAi7qHWla09_rGeQraiLpH-aY,491
|
|
10
|
+
django_ninja_aio_crud-0.6.3.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
11
|
+
django_ninja_aio_crud-0.6.3.dist-info/METADATA,sha256=26XLVzdo5EKej1jre04eUxspawzraIVI4LbRm8Qqymc,13740
|
|
12
|
+
django_ninja_aio_crud-0.6.3.dist-info/RECORD,,
|
ninja_aio/__init__.py
CHANGED
ninja_aio/auth.py
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
from joserfc import jwt, jwk
|
|
1
|
+
from joserfc import jwt, jwk
|
|
2
2
|
from django.http.request import HttpRequest
|
|
3
3
|
from ninja.security.http import HttpBearer
|
|
4
4
|
|
|
5
|
-
from .exceptions import AuthError
|
|
5
|
+
from .exceptions import AuthError
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class AsyncJwtBearer(HttpBearer):
|
|
@@ -16,15 +16,7 @@ class AsyncJwtBearer(HttpBearer):
|
|
|
16
16
|
|
|
17
17
|
def validate_claims(self, claims: jwt.Claims):
|
|
18
18
|
jwt_claims = self.get_claims()
|
|
19
|
-
|
|
20
|
-
try:
|
|
21
|
-
jwt_claims.validate(claims)
|
|
22
|
-
except (
|
|
23
|
-
errors.InvalidClaimError,
|
|
24
|
-
errors.MissingClaimError,
|
|
25
|
-
errors.ExpiredTokenError,
|
|
26
|
-
) as exc:
|
|
27
|
-
raise AuthError(**parse_jose_error(exc), status_code=401)
|
|
19
|
+
jwt_claims.validate(claims)
|
|
28
20
|
|
|
29
21
|
async def auth_handler(self, request: HttpRequest):
|
|
30
22
|
"""
|
|
@@ -35,8 +27,6 @@ class AsyncJwtBearer(HttpBearer):
|
|
|
35
27
|
async def authenticate(self, request: HttpRequest, token: str):
|
|
36
28
|
try:
|
|
37
29
|
self.dcd = jwt.decode(token, self.jwt_public, algorithms=self.algorithms)
|
|
38
|
-
except errors.BadSignatureError as exc:
|
|
39
|
-
raise AuthError(**parse_jose_error(exc), status_code=401)
|
|
40
30
|
except ValueError as exc:
|
|
41
31
|
raise AuthError(", ".join(exc.args), 401)
|
|
42
32
|
|
ninja_aio/exceptions.py
CHANGED
|
@@ -53,8 +53,16 @@ def _pydantic_validation_error(
|
|
|
53
53
|
return api.create_response(request, error.error, status=error.status_code)
|
|
54
54
|
|
|
55
55
|
|
|
56
|
+
def _jose_error(
|
|
57
|
+
request: HttpRequest, exc: JoseError, api: type[NinjaAPI]
|
|
58
|
+
) -> HttpResponse:
|
|
59
|
+
error = BaseException(**parse_jose_error(exc), status_code=401)
|
|
60
|
+
return api.create_response(request, error.error, status=error.status_code)
|
|
61
|
+
|
|
62
|
+
|
|
56
63
|
def set_api_exception_handlers(api: type[NinjaAPI]) -> None:
|
|
57
64
|
api.add_exception_handler(BaseException, partial(_default_error, api=api))
|
|
65
|
+
api.add_exception_handler(JoseError, partial(_jose_error, api=api))
|
|
58
66
|
api.add_exception_handler(
|
|
59
67
|
ValidationError, partial(_pydantic_validation_error, api=api)
|
|
60
68
|
)
|
ninja_aio/models.py
CHANGED
|
@@ -51,13 +51,14 @@ class ModelUtil:
|
|
|
51
51
|
pk: int | str = None,
|
|
52
52
|
filters: dict = None,
|
|
53
53
|
getters: dict = None,
|
|
54
|
+
with_qs_request=True,
|
|
54
55
|
):
|
|
55
56
|
get_q = {self.model_pk_name: pk} if pk is not None else {}
|
|
56
57
|
if getters:
|
|
57
58
|
get_q |= getters
|
|
58
59
|
|
|
59
60
|
obj_qs = self.model.objects.select_related()
|
|
60
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
61
|
+
if isinstance(self.model, ModelSerializerMeta) and with_qs_request:
|
|
61
62
|
obj_qs = await self.model.queryset_request(request)
|
|
62
63
|
|
|
63
64
|
obj_qs = obj_qs.prefetch_related(*self.get_reverse_relations())
|
|
@@ -108,7 +109,9 @@ class ModelUtil:
|
|
|
108
109
|
raise SerializeError({k: ". ".join(exc.args)}, 400)
|
|
109
110
|
if isinstance(field_obj, models.ForeignKey):
|
|
110
111
|
rel_util = ModelUtil(field_obj.related_model)
|
|
111
|
-
rel: ModelSerializer = await rel_util.get_object(
|
|
112
|
+
rel: ModelSerializer = await rel_util.get_object(
|
|
113
|
+
request, v, with_qs_request=False
|
|
114
|
+
)
|
|
112
115
|
payload |= {k: rel}
|
|
113
116
|
new_payload = {
|
|
114
117
|
k: v for k, v in payload.items() if k not in (customs.keys() or optionals)
|
|
@@ -122,14 +125,17 @@ class ModelUtil:
|
|
|
122
125
|
try:
|
|
123
126
|
field_obj = getattr(self.model, k).field
|
|
124
127
|
except AttributeError:
|
|
125
|
-
|
|
128
|
+
try:
|
|
129
|
+
field_obj = getattr(self.model, k).related
|
|
130
|
+
except AttributeError:
|
|
131
|
+
pass
|
|
126
132
|
if isinstance(v, dict) and (
|
|
127
133
|
isinstance(field_obj, models.ForeignKey)
|
|
128
134
|
or isinstance(field_obj, models.OneToOneField)
|
|
129
135
|
):
|
|
130
136
|
rel_util = ModelUtil(field_obj.related_model)
|
|
131
137
|
rel: ModelSerializer = await rel_util.get_object(
|
|
132
|
-
request, list(v.values())[0]
|
|
138
|
+
request, list(v.values())[0], with_qs_request=False
|
|
133
139
|
)
|
|
134
140
|
if isinstance(field_obj, models.ForeignKey):
|
|
135
141
|
for rel_k, rel_v in v.items():
|
|
@@ -196,6 +202,7 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
196
202
|
class ReadSerializer:
|
|
197
203
|
fields: list[str] = []
|
|
198
204
|
excludes: list[str] = []
|
|
205
|
+
customs: list[tuple[str, type, Any]] = []
|
|
199
206
|
|
|
200
207
|
class UpdateSerializer:
|
|
201
208
|
fields: list[str] = []
|
|
@@ -257,15 +264,15 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
257
264
|
case "Patch":
|
|
258
265
|
s_type = "update"
|
|
259
266
|
case "Out":
|
|
260
|
-
fields, reverse_rels, excludes = cls.get_schema_out_data()
|
|
261
|
-
if not fields and not reverse_rels and not excludes:
|
|
267
|
+
fields, reverse_rels, excludes, customs = cls.get_schema_out_data()
|
|
268
|
+
if not fields and not reverse_rels and not excludes and not customs:
|
|
262
269
|
return None
|
|
263
270
|
return create_schema(
|
|
264
271
|
model=cls,
|
|
265
272
|
name=f"{cls._meta.model_name}SchemaOut",
|
|
266
273
|
depth=depth,
|
|
267
274
|
fields=fields,
|
|
268
|
-
custom_fields=reverse_rels,
|
|
275
|
+
custom_fields=reverse_rels + customs,
|
|
269
276
|
exclude=excludes,
|
|
270
277
|
)
|
|
271
278
|
fields = cls.get_fields(s_type)
|
|
@@ -367,25 +374,36 @@ class ModelSerializer(models.Model, metaclass=ModelSerializerMeta):
|
|
|
367
374
|
reverse_rels = []
|
|
368
375
|
for f in cls.get_fields("read"):
|
|
369
376
|
field_obj = getattr(cls, f)
|
|
370
|
-
if isinstance(
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
377
|
+
if isinstance(
|
|
378
|
+
field_obj,
|
|
379
|
+
(
|
|
380
|
+
ManyToManyDescriptor,
|
|
381
|
+
ReverseManyToOneDescriptor,
|
|
382
|
+
ReverseOneToOneDescriptor,
|
|
383
|
+
),
|
|
384
|
+
):
|
|
385
|
+
if isinstance(field_obj, ManyToManyDescriptor):
|
|
386
|
+
rel_obj: ModelSerializer = field_obj.field.related_model
|
|
387
|
+
if field_obj.reverse:
|
|
388
|
+
rel_obj = field_obj.field.model
|
|
389
|
+
rel_type = "many"
|
|
390
|
+
elif isinstance(field_obj, ReverseManyToOneDescriptor):
|
|
391
|
+
rel_obj = field_obj.field.model
|
|
392
|
+
rel_type = "many"
|
|
393
|
+
else: # ReverseOneToOneDescriptor
|
|
394
|
+
rel_obj = field_obj.related.related_model
|
|
395
|
+
rel_type = "one"
|
|
396
|
+
|
|
397
|
+
rel_data = cls.get_reverse_relation_schema(rel_obj, rel_type, f)
|
|
385
398
|
reverse_rels.append(rel_data)
|
|
386
399
|
continue
|
|
387
400
|
fields.append(f)
|
|
388
|
-
return
|
|
401
|
+
return (
|
|
402
|
+
fields,
|
|
403
|
+
reverse_rels,
|
|
404
|
+
cls.get_excluded_fields("read"),
|
|
405
|
+
cls.get_custom_fields("read"),
|
|
406
|
+
)
|
|
389
407
|
|
|
390
408
|
@classmethod
|
|
391
409
|
def is_custom(cls, field: str):
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
ninja_aio/__init__.py,sha256=VSJ4PO3jZ-Z_NF8y2IrjsTc6MSqxlJ_b7TtAvZW4f28,119
|
|
2
|
-
ninja_aio/api.py,sha256=Fe6l3YCy7MW5TY4-Lbl80CFuK2NT2Y7tHfmqPk6Mqak,1735
|
|
3
|
-
ninja_aio/auth.py,sha256=z9gniLIgT8SjRqhGN7ZI0AGHjsALwgU6eyr2m46fwFY,1389
|
|
4
|
-
ninja_aio/exceptions.py,sha256=Lg0nUJe6kWEf0qvXpQ9FqQ8sCFBFH-lP4xovvY-YfiY,1922
|
|
5
|
-
ninja_aio/models.py,sha256=lplwxtZKweoppJEi0ERPSHRtsq5UwQaGK3NtR_I-FBw,15213
|
|
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=EHznS-6KWLwSX5hLeXbAi7qHWla09_rGeQraiLpH-aY,491
|
|
10
|
-
ninja_aio/views.py,sha256=z820mKzfbvh9SZ4cALQVKMfc64kx74t8xWGWaUBzHfs,9270
|
|
11
|
-
django_ninja_aio_crud-0.6.1.dist-info/WHEEL,sha256=CpUCUxeHQbRN5UGRQHYRJorO5Af-Qy_fHMctcQ8DSGI,82
|
|
12
|
-
django_ninja_aio_crud-0.6.1.dist-info/METADATA,sha256=4nH4p3l9UUIUGilQ4C_fDM1Uv-WiF6FveqrG25JosQk,13076
|
|
13
|
-
django_ninja_aio_crud-0.6.1.dist-info/RECORD,,
|
ninja_aio/views.py
DELETED
|
@@ -1,271 +0,0 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
|
-
from ninja import NinjaAPI, Router, Schema, Path, Query
|
|
4
|
-
from ninja.constants import NOT_SET
|
|
5
|
-
from ninja.pagination import paginate, AsyncPaginationBase, PageNumberPagination
|
|
6
|
-
from django.http import HttpRequest
|
|
7
|
-
from django.db.models import Model, QuerySet
|
|
8
|
-
from pydantic import create_model
|
|
9
|
-
|
|
10
|
-
from .models import ModelSerializer, ModelUtil
|
|
11
|
-
from .schemas import GenericMessageSchema
|
|
12
|
-
from .types import ModelSerializerMeta, VIEW_TYPES
|
|
13
|
-
|
|
14
|
-
ERROR_CODES = frozenset({400, 401, 404, 428})
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class APIView:
|
|
18
|
-
api: NinjaAPI
|
|
19
|
-
router_tag: str
|
|
20
|
-
api_route_path: str
|
|
21
|
-
auths: list | None = NOT_SET
|
|
22
|
-
|
|
23
|
-
def __init__(self) -> None:
|
|
24
|
-
self.router = Router(tags=[self.router_tag])
|
|
25
|
-
self.error_codes = ERROR_CODES
|
|
26
|
-
|
|
27
|
-
def views(self):
|
|
28
|
-
"""
|
|
29
|
-
Override this method to add your custom views. For example:
|
|
30
|
-
@self.router.get(some_path, response=some_schema)
|
|
31
|
-
async def some_method(request, *args, **kwargs):
|
|
32
|
-
pass
|
|
33
|
-
|
|
34
|
-
You can add multilple views just doing:
|
|
35
|
-
|
|
36
|
-
@self.router.get(some_path, response=some_schema)
|
|
37
|
-
async def some_method(request, *args, **kwargs):
|
|
38
|
-
pass
|
|
39
|
-
|
|
40
|
-
@self.router.post(some_path, response=some_schema)
|
|
41
|
-
async def some_method(request, *args, **kwargs):
|
|
42
|
-
pass
|
|
43
|
-
|
|
44
|
-
If you provided a list of auths you can chose which of your views
|
|
45
|
-
should be authenticated:
|
|
46
|
-
|
|
47
|
-
AUTHENTICATED VIEW:
|
|
48
|
-
|
|
49
|
-
@self.router.get(some_path, response=some_schema, auth=self.auths)
|
|
50
|
-
async def some_method(request, *args, **kwargs):
|
|
51
|
-
pass
|
|
52
|
-
|
|
53
|
-
NOT AUTHENTICATED VIEW:
|
|
54
|
-
|
|
55
|
-
@self.router.post(some_path, response=some_schema)
|
|
56
|
-
async def some_method(request, *args, **kwargs):
|
|
57
|
-
pass
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
def add_views(self):
|
|
61
|
-
self.views()
|
|
62
|
-
return self.router
|
|
63
|
-
|
|
64
|
-
def add_views_to_route(self):
|
|
65
|
-
return self.api.add_router(f"{self.api_route_path}/", self.add_views())
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
class APIViewSet:
|
|
69
|
-
model: ModelSerializer | Model
|
|
70
|
-
api: NinjaAPI
|
|
71
|
-
schema_in: Schema | None = None
|
|
72
|
-
schema_out: Schema | None = None
|
|
73
|
-
schema_update: Schema | None = None
|
|
74
|
-
auths: list | None = NOT_SET
|
|
75
|
-
pagination_class: type[AsyncPaginationBase] = PageNumberPagination
|
|
76
|
-
query_params: dict[str, tuple[type, ...]] = {}
|
|
77
|
-
disable: list[type[VIEW_TYPES]] = []
|
|
78
|
-
|
|
79
|
-
def __init__(self) -> None:
|
|
80
|
-
self.error_codes = ERROR_CODES
|
|
81
|
-
self.model_util = ModelUtil(self.model)
|
|
82
|
-
self.schema_out, self.schema_in, self.schema_update = self.get_schemas()
|
|
83
|
-
self.path_schema = self._generate_path_schema()
|
|
84
|
-
self.filters_schema = self._generate_filters_schema()
|
|
85
|
-
self.router_tag = self.model_util.model_name.capitalize()
|
|
86
|
-
self.router = Router(tags=[self.router_tag])
|
|
87
|
-
self.path = "/"
|
|
88
|
-
self.path_retrieve = f"{{{self.model_util.model_pk_name}}}/"
|
|
89
|
-
|
|
90
|
-
@property
|
|
91
|
-
def _crud_views(self):
|
|
92
|
-
"""
|
|
93
|
-
key: view type (create, list, retrieve, update, delete or all)
|
|
94
|
-
value: tuple with schema and view method
|
|
95
|
-
"""
|
|
96
|
-
return {
|
|
97
|
-
"create": (self.schema_in, self.create_view),
|
|
98
|
-
"list": (self.schema_out, self.list_view),
|
|
99
|
-
"retrieve": (self.schema_out, self.retrieve_view),
|
|
100
|
-
"update": (self.schema_update, self.update_view),
|
|
101
|
-
"delete": (None, self.delete_view),
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
def _generate_schema(self, fields: dict, name: str) -> Schema:
|
|
105
|
-
return create_model(f"{self.model_util.model_name}{name}", **fields)
|
|
106
|
-
|
|
107
|
-
def _generate_path_schema(self):
|
|
108
|
-
return self._generate_schema(
|
|
109
|
-
{self.model_util.model_pk_name: (int | str, ...)}, "PathSchema"
|
|
110
|
-
)
|
|
111
|
-
|
|
112
|
-
def _generate_filters_schema(self):
|
|
113
|
-
return self._generate_schema(self.query_params, "FiltersSchema")
|
|
114
|
-
|
|
115
|
-
def _get_pk(self, data: Schema):
|
|
116
|
-
return data.model_dump()[self.model_util.model_pk_name]
|
|
117
|
-
|
|
118
|
-
def get_schemas(self):
|
|
119
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
120
|
-
return (
|
|
121
|
-
self.model.generate_read_s(),
|
|
122
|
-
self.model.generate_create_s(),
|
|
123
|
-
self.model.generate_update_s(),
|
|
124
|
-
)
|
|
125
|
-
return self.schema_out, self.schema_in, self.schema_update
|
|
126
|
-
|
|
127
|
-
async def query_params_handler(
|
|
128
|
-
self, queryset: QuerySet[ModelSerializer], filters: dict
|
|
129
|
-
):
|
|
130
|
-
"""
|
|
131
|
-
Override this method to handle request query params making queries to the database
|
|
132
|
-
based on filters or any other logic. This method should return a queryset. filters
|
|
133
|
-
are given already dumped by the schema.
|
|
134
|
-
"""
|
|
135
|
-
return queryset
|
|
136
|
-
|
|
137
|
-
def create_view(self):
|
|
138
|
-
@self.router.post(
|
|
139
|
-
self.path,
|
|
140
|
-
auth=self.auths,
|
|
141
|
-
response={201: self.schema_out, self.error_codes: GenericMessageSchema},
|
|
142
|
-
)
|
|
143
|
-
async def create(request: HttpRequest, data: self.schema_in):
|
|
144
|
-
return 201, await self.model_util.create_s(request, data, self.schema_out)
|
|
145
|
-
|
|
146
|
-
create.__name__ = f"create_{self.model_util.model_name}"
|
|
147
|
-
return create
|
|
148
|
-
|
|
149
|
-
def list_view(self):
|
|
150
|
-
@self.router.get(
|
|
151
|
-
self.path,
|
|
152
|
-
auth=self.auths,
|
|
153
|
-
response={
|
|
154
|
-
200: List[self.schema_out],
|
|
155
|
-
self.error_codes: GenericMessageSchema,
|
|
156
|
-
},
|
|
157
|
-
)
|
|
158
|
-
@paginate(self.pagination_class)
|
|
159
|
-
async def list(
|
|
160
|
-
request: HttpRequest, filters: Query[self.filters_schema] = None
|
|
161
|
-
):
|
|
162
|
-
qs = self.model.objects.select_related()
|
|
163
|
-
if isinstance(self.model, ModelSerializerMeta):
|
|
164
|
-
qs = await self.model.queryset_request(request)
|
|
165
|
-
rels = self.model_util.get_reverse_relations()
|
|
166
|
-
if len(rels) > 0:
|
|
167
|
-
qs = qs.prefetch_related(*rels)
|
|
168
|
-
if filters is not None:
|
|
169
|
-
qs = await self.query_params_handler(qs, filters.model_dump())
|
|
170
|
-
objs = [
|
|
171
|
-
await self.model_util.read_s(request, obj, self.schema_out)
|
|
172
|
-
async for obj in qs.all()
|
|
173
|
-
]
|
|
174
|
-
return objs
|
|
175
|
-
|
|
176
|
-
list.__name__ = f"list_{self.model_util.verbose_name_view_resolver()}"
|
|
177
|
-
return list
|
|
178
|
-
|
|
179
|
-
def retrieve_view(self):
|
|
180
|
-
@self.router.get(
|
|
181
|
-
self.path_retrieve,
|
|
182
|
-
auth=self.auths,
|
|
183
|
-
response={200: self.schema_out, self.error_codes: GenericMessageSchema},
|
|
184
|
-
)
|
|
185
|
-
async def retrieve(request: HttpRequest, pk: Path[self.path_schema]):
|
|
186
|
-
obj = await self.model_util.get_object(request, self._get_pk(pk))
|
|
187
|
-
return await self.model_util.read_s(request, obj, self.schema_out)
|
|
188
|
-
|
|
189
|
-
retrieve.__name__ = f"retrieve_{self.model_util.model_name}"
|
|
190
|
-
return retrieve
|
|
191
|
-
|
|
192
|
-
def update_view(self):
|
|
193
|
-
@self.router.patch(
|
|
194
|
-
self.path_retrieve,
|
|
195
|
-
auth=self.auths,
|
|
196
|
-
response={200: self.schema_out, self.error_codes: GenericMessageSchema},
|
|
197
|
-
)
|
|
198
|
-
async def update(
|
|
199
|
-
request: HttpRequest, data: self.schema_update, pk: Path[self.path_schema]
|
|
200
|
-
):
|
|
201
|
-
return await self.model_util.update_s(
|
|
202
|
-
request, data, self._get_pk(pk), self.schema_out
|
|
203
|
-
)
|
|
204
|
-
|
|
205
|
-
update.__name__ = f"update_{self.model_util.model_name}"
|
|
206
|
-
return update
|
|
207
|
-
|
|
208
|
-
def delete_view(self):
|
|
209
|
-
@self.router.delete(
|
|
210
|
-
self.path_retrieve,
|
|
211
|
-
auth=self.auths,
|
|
212
|
-
response={204: None, self.error_codes: GenericMessageSchema},
|
|
213
|
-
)
|
|
214
|
-
async def delete(request: HttpRequest, pk: Path[self.path_schema]):
|
|
215
|
-
return 204, await self.model_util.delete_s(request, self._get_pk(pk))
|
|
216
|
-
|
|
217
|
-
delete.__name__ = f"delete_{self.model_util.model_name}"
|
|
218
|
-
return delete
|
|
219
|
-
|
|
220
|
-
def views(self):
|
|
221
|
-
"""
|
|
222
|
-
Override this method to add your custom views. For example:
|
|
223
|
-
@self.router.get(some_path, response=some_schema)
|
|
224
|
-
async def some_method(request, *args, **kwargs):
|
|
225
|
-
pass
|
|
226
|
-
|
|
227
|
-
You can add multilple views just doing:
|
|
228
|
-
|
|
229
|
-
@self.router.get(some_path, response=some_schema)
|
|
230
|
-
async def some_method(request, *args, **kwargs):
|
|
231
|
-
pass
|
|
232
|
-
|
|
233
|
-
@self.router.post(some_path, response=some_schema)
|
|
234
|
-
async def some_method(request, *args, **kwargs):
|
|
235
|
-
pass
|
|
236
|
-
|
|
237
|
-
If you provided a list of auths you can chose which of your views
|
|
238
|
-
should be authenticated:
|
|
239
|
-
|
|
240
|
-
AUTHENTICATED VIEW:
|
|
241
|
-
|
|
242
|
-
@self.router.get(some_path, response=some_schema, auth=self.auths)
|
|
243
|
-
async def some_method(request, *args, **kwargs):
|
|
244
|
-
pass
|
|
245
|
-
|
|
246
|
-
NOT AUTHENTICATED VIEW:
|
|
247
|
-
|
|
248
|
-
@self.router.post(some_path, response=some_schema)
|
|
249
|
-
async def some_method(request, *args, **kwargs):
|
|
250
|
-
pass
|
|
251
|
-
"""
|
|
252
|
-
|
|
253
|
-
def add_views(self):
|
|
254
|
-
if "all" in self.disable:
|
|
255
|
-
self.views()
|
|
256
|
-
return self.router
|
|
257
|
-
|
|
258
|
-
for views_type, (schema, view) in self._crud_views.items():
|
|
259
|
-
if views_type not in self.disable and (
|
|
260
|
-
schema is not None or views_type == "delete"
|
|
261
|
-
):
|
|
262
|
-
view()
|
|
263
|
-
|
|
264
|
-
self.views()
|
|
265
|
-
return self.router
|
|
266
|
-
|
|
267
|
-
def add_views_to_route(self):
|
|
268
|
-
return self.api.add_router(
|
|
269
|
-
f"{self.model_util.verbose_name_path_resolver()}/",
|
|
270
|
-
self.add_views(),
|
|
271
|
-
)
|
|
File without changes
|