django-ninja-aio-crud 0.1.3__tar.gz → 0.2.0__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.3
3
+ Version: 0.2.0
4
4
  Summary: Django Ninja AIO CRUD - Rest Framework
5
5
  Author: Giuseppe Casillo
6
6
  Requires-Python: >=3.10
@@ -300,11 +300,11 @@ BarAPI().add_views_to_route()
300
300
 
301
301
  - Foo Schemas
302
302
 
303
- ![Swagger UI](ninja_aio/docs/images/foo-swagger.png)
303
+ ![Swagger UI](docs/images/foo-swagger.png)
304
304
 
305
305
  - Bar Schemas with reverse relation
306
306
 
307
- ![Swagger UI](ninja_aio/docs/images/bar-swagger.png)
307
+ ![Swagger UI](docs/images/bar-swagger.png)
308
308
 
309
309
  ## 🔒 Authentication
310
310
 
@@ -271,11 +271,11 @@ BarAPI().add_views_to_route()
271
271
 
272
272
  - Foo Schemas
273
273
 
274
- ![Swagger UI](ninja_aio/docs/images/foo-swagger.png)
274
+ ![Swagger UI](docs/images/foo-swagger.png)
275
275
 
276
276
  - Bar Schemas with reverse relation
277
277
 
278
- ![Swagger UI](ninja_aio/docs/images/bar-swagger.png)
278
+ ![Swagger UI](docs/images/bar-swagger.png)
279
279
 
280
280
  ## 🔒 Authentication
281
281
 
@@ -1,3 +1,3 @@
1
1
  """ Django Ninja AIO CRUD - Rest Framework """
2
2
 
3
- __version__ = "0.1.3"
3
+ __version__ = "0.2.0"
@@ -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,22 +90,40 @@ 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
- reverse_rels.append(field_obj.field.__dict__.get("_related_name"))
97
+ reverse_rels.append(field_obj.field._related_name)
98
+ continue
91
99
  if isinstance(field_obj, ReverseOneToOneDescriptor):
92
- reverse_rels.append(
93
- list(field_obj.__dict__.values())[0].__dict__.get("related_name")
94
- )
100
+ reverse_rels.append(field_obj.related.name)
95
101
  return reverse_rels
96
102
 
97
103
  @classmethod
98
104
  def get_reverse_relation_schema(
99
105
  cls, obj: type["ModelSerializer"], rel_type: type[REL_TYPES], field: str
100
106
  ):
101
- for index, rel_f in enumerate(obj.ReadSerializer.fields):
102
- if rel_f == cls._meta.model_name:
103
- obj.ReadSerializer.fields.pop(index)
104
- break
107
+ cls_f = []
108
+ for rel_f in obj.ReadSerializer.fields:
109
+ rel_f_obj = getattr(obj, rel_f)
110
+ if (
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
119
+ ):
120
+ cls_f.append(rel_f)
121
+ obj.ReadSerializer.fields.remove(rel_f)
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
+
105
127
  rel_schema = obj.generate_read_s(depth=0)
106
128
  if rel_type == "many":
107
129
  rel_schema = list[rel_schema]
@@ -110,7 +132,8 @@ class ModelSerializer(models.Model):
110
132
  rel_schema | None,
111
133
  None,
112
134
  )
113
- obj.ReadSerializer.fields.append(cls._meta.model_name)
135
+ if len(cls_f) > 0:
136
+ obj.ReadSerializer.fields.append(*cls_f)
114
137
  return rel_data
115
138
 
116
139
  @classmethod
@@ -119,15 +142,20 @@ class ModelSerializer(models.Model):
119
142
  reverse_rels = []
120
143
  for f in cls.ReadSerializer.fields:
121
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
122
152
  if isinstance(field_obj, ReverseManyToOneDescriptor):
123
- rel_obj: ModelSerializer = field_obj.field.__dict__.get("model")
153
+ rel_obj: ModelSerializer = field_obj.field.model
124
154
  rel_data = cls.get_reverse_relation_schema(rel_obj, "many", f)
125
155
  reverse_rels.append(rel_data)
126
156
  continue
127
157
  if isinstance(field_obj, ReverseOneToOneDescriptor):
128
- rel_obj: ModelSerializer = list(field_obj.__dict__.values())[
129
- 0
130
- ].__dict__.get("related_model")
158
+ rel_obj: ModelSerializer = field_obj.related.related_model
131
159
  rel_data = cls.get_reverse_relation_schema(rel_obj, "one", f)
132
160
  reverse_rels.append(rel_data)
133
161
  continue
@@ -149,9 +177,10 @@ class ModelSerializer(models.Model):
149
177
  continue
150
178
  field_obj = getattr(cls, k).field
151
179
  if isinstance(field_obj, models.BinaryField):
152
- if not v.endswith(b"=="):
153
- v = v + b"=="
154
- payload |= {k: base64.b64decode(v)}
180
+ try:
181
+ payload |= {k: base64.b64decode(v)}
182
+ except Exception as exc:
183
+ raise SerializeError({k: ". ".join(exc.args)}, 400)
155
184
  if isinstance(field_obj, models.ForeignKey):
156
185
  try:
157
186
  rel: ModelSerializer = await field_obj.related_model.get_object(
@@ -174,7 +203,7 @@ class ModelSerializer(models.Model):
174
203
  field_obj = getattr(cls, k).related
175
204
  if isinstance(v, dict) and (
176
205
  isinstance(field_obj, models.ForeignKey)
177
- or isinstance(field_obj, OneToOneRel)
206
+ or isinstance(field_obj, models.OneToOneField)
178
207
  ):
179
208
  rel: ModelSerializer = await field_obj.related_model.get_object(
180
209
  request, list(v.values())[0]
@@ -276,10 +305,10 @@ class ModelSerializer(models.Model):
276
305
  for k, v in payload.items():
277
306
  if v is not None:
278
307
  setattr(obj, k, v)
279
- payload |= customs
280
- await obj.custom_actions(payload)
308
+ await obj.custom_actions(customs)
281
309
  await obj.asave()
282
- 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)
283
312
 
284
313
  @classmethod
285
314
  async def delete_s(cls, request: HttpRequest, pk: int | str):
@@ -8,9 +8,11 @@ from ninja.renderers import BaseRenderer
8
8
  class ORJSONRenderer(BaseRenderer):
9
9
  media_type = "application/json"
10
10
 
11
- def render(self, request: HttpRequest, data: dict | list, *, response_status):
12
- if isinstance(data, list):
13
- return orjson.dumps(self.render_list(data))
11
+ def render(self, request: HttpRequest, data: dict, *, response_status):
12
+ old_d = data
13
+ for k, v in old_d.items():
14
+ if isinstance(v, list):
15
+ data |= {k: self.render_list(v)}
14
16
  return orjson.dumps(self.render_dict(data))
15
17
 
16
18
  @classmethod
@@ -33,11 +35,9 @@ class ORJSONRenderer(BaseRenderer):
33
35
  v |= {k_rel: base64.b64encode(v_rel).decode()}
34
36
  data |= {k: v}
35
37
  if isinstance(v, list):
36
- index_rel = 0
37
- for f_rel in v:
38
+ for index_rel, f_rel in enumerate(v):
38
39
  for k_rel, v_rel in f_rel.items():
39
40
  if isinstance(v_rel, bytes):
40
41
  v[index_rel] |= {k_rel: base64.b64encode(v_rel).decode()}
41
- index_rel += 1
42
42
  data |= {k: v}
43
43
  return data
@@ -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
  )