django-ninja-aio-crud 0.1.2__tar.gz → 0.1.3__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.2
3
+ Version: 0.1.3
4
4
  Summary: Django Ninja AIO CRUD - Rest Framework
5
5
  Author: Giuseppe Casillo
6
6
  Requires-Python: >=3.10
@@ -34,24 +34,32 @@ Project-URL: Repository, https://github.com/caspel26/django-ninja-aio-crud
34
34
  ## 📝 Instructions
35
35
 
36
36
  ### 📚 Prerequisites
37
+
37
38
  - Install Python from the [official website](https://www.python.org/) (latest version) and ensure it is added to the system Path and environment variables.
38
39
 
39
40
  ### 💻 Setup your environment
41
+
40
42
  - Create a virtual environment
41
43
  ```bash
42
44
  python -m venv .venv
43
45
  ```
46
+
44
47
  ### ✅ Activate it
48
+
45
49
  - If you are from linux activate it with
50
+
46
51
  ```bash
47
52
  . .venv/bin/activate
48
53
  ```
54
+
49
55
  - If you are from windows activate it with
56
+
50
57
  ```bash
51
58
  . .venv/Scripts/activate
52
59
  ```
53
60
 
54
61
  ### 📥 Install package
62
+
55
63
  ```bash
56
64
  pip install django-ninja-aio-crud
57
65
  ```
@@ -63,14 +71,17 @@ pip install django-ninja-aio-crud
63
71
  > and why not ... [Buy me a coffee](https://buymeacoffee.com/caspel26)
64
72
 
65
73
  ### ModelSerializer
74
+
66
75
  - You can serialize your models using ModelSerializer and made them inherit from it. In your models.py import ModelSerializer
67
76
  ```Python
77
+ # models.py
78
+ from django.db import models
68
79
  from ninja_aio.models import ModelSerializer
69
80
 
70
81
 
71
82
  class Foo(ModelSerializer):
72
- name = mdoels.CharField()
73
- bar = models.CharField()
83
+ name = models.CharField(max_length=30)
84
+ bar = models.CharField(max_length=30)
74
85
 
75
86
  class ReadSerializer:
76
87
  fields = ["id", "name", "bar"]
@@ -81,14 +92,18 @@ class Foo(ModelSerializer):
81
92
  class UpdateSerializer:
82
93
  fields = ["name", "bar"]
83
94
  ```
95
+
84
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
+
85
98
  ```Python
99
+ # models.py
100
+ from django.db import models
86
101
  from ninja_aio.models import ModelSerializer
87
102
 
88
103
 
89
104
  class Foo(ModelSerializer):
90
- name = mdoels.CharField()
91
- bar = models.CharField()
105
+ name = models.CharField(max_length=30)
106
+ bar = models.CharField(max_length=30)
92
107
  active = models.BooleanField(default=False)
93
108
 
94
109
  class ReadSerializer:
@@ -112,12 +127,15 @@ class Foo(ModelSerializer):
112
127
  self.active = True
113
128
  await self.asave()
114
129
  ```
115
- - 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.
116
130
 
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.
117
132
 
118
133
  ### APIViewSet
134
+
119
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
+
120
137
  ```Python
138
+ # views.py
121
139
  from ninja import NinjAPI
122
140
  from ninja_aio.views import APIViewSet
123
141
  from ninja_aio.parsers import ORJSONParser
@@ -135,8 +153,11 @@ class FooAPI(APIViewSet):
135
153
 
136
154
  FooAPI().add_views_to_route()
137
155
  ```
156
+
138
157
  - 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".
158
+
139
159
  ```Python
160
+ # views.py
140
161
  from ninja import NinjAPI, Schema
141
162
  from ninja_aio.views import APIViewSet
142
163
  from ninja_aio.parsers import ORJSONParser
@@ -161,7 +182,7 @@ class FooAPI(APIViewSet):
161
182
  api = api
162
183
 
163
184
  def views(self):
164
- @self.router.post("numbers-sum/", response={200: ExampleSchemaOut)
185
+ @self.router.post("numbers-sum/", response={200: ExampleSchemaOut})
165
186
  async def sum(request: HttpRequest, data: ExampleSchemaIn):
166
187
  return 200, {sum: data.n1 + data.n2}
167
188
 
@@ -170,8 +191,11 @@ FooAPI().add_views_to_route()
170
191
  ```
171
192
 
172
193
  ### APIView
194
+
173
195
  - View class to code generic views class based. In your views.py import APIView class.
196
+
174
197
  ```Python
198
+ # views.py
175
199
  from ninja import NinjAPI, Schema
176
200
  from ninja_aio.views import APIView
177
201
  from ninja_aio.parsers import ORJSONParser
@@ -195,7 +219,7 @@ class SumView(APIView):
195
219
  router_tag = "Sum"
196
220
 
197
221
  def views(self):
198
- @self.router.post(self.api_router_path, response={200: ExampleSchemaOut)
222
+ @self.router.post("/", response={200: ExampleSchemaOut})
199
223
  async def sum(request: HttpRequest, data: ExampleSchemaIn):
200
224
  return 200, {sum: data.n1 + data.n2}
201
225
 
@@ -203,9 +227,91 @@ class SumView(APIView):
203
227
  SumView().add_views_to_route()
204
228
  ```
205
229
 
230
+ ### 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.
235
+
236
+ - Define models:
237
+
238
+ ```Python
239
+ # models.py
240
+ class Bar(ModelSerializer):
241
+ name = models.CharField(max_length=30)
242
+ description = models.TextField(max_length=30)
243
+
244
+ # ReadSerializer with reverse OneToMany relation (foos)
245
+ class ReadSerializer:
246
+ fields = ["id", "name", "description", "foos"]
247
+
248
+ class CreateSerializer:
249
+ fields = ["name", "description"]
250
+
251
+ class UpdateSerializer:
252
+ fields = ["name", "description"]
253
+
254
+
255
+ class Foo(ModelSerializer):
256
+ name = models.CharField(max_length=30)
257
+ bar = models.ForeignKey(Bar, on_delete=models.CASCADE, related_name="foos")
258
+
259
+ class ReadSerializer:
260
+ fields = ["id", "name", "bar"]
261
+
262
+ class CreateSerializer:
263
+ fields = ["name", "bar"]
264
+
265
+ class UpdateSerializer:
266
+ fields = ["name"]
267
+ ```
268
+
269
+ - Define views:
270
+
271
+ ```Python
272
+ # views.py
273
+ from ninja import NinjAPI
274
+ from ninja_aio.views import APIViewSet
275
+ from ninja_aio.parsers import ORJSONParser
276
+ from ninja_aio.renders import ORJSONRender
277
+
278
+ from .models import Foo, Bar
279
+
280
+ api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
281
+
282
+
283
+ class FooAPI(APIViewSet):
284
+ model = Foo
285
+ api = api
286
+
287
+
288
+ class BarAPI(APIViewSet):
289
+ model = Bar
290
+ api = api
291
+
292
+
293
+ FooAPI().add_views_to_route()
294
+ BarAPI().add_views_to_route()
295
+ ```
296
+
297
+ - Now run your server and go to /docs url:
298
+
299
+ ### Docs
300
+
301
+ - Foo Schemas
302
+
303
+ ![Swagger UI](ninja_aio/docs/images/foo-swagger.png)
304
+
305
+ - Bar Schemas with reverse relation
306
+
307
+ ![Swagger UI](ninja_aio/docs/images/bar-swagger.png)
308
+
206
309
  ## 🔒 Authentication
310
+
207
311
  ### Jwt
312
+
208
313
  - 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
+
209
315
  ```Python
210
316
  from ninja_aio.auth import AsyncJWTBearer
211
317
  from django.conf import settings
@@ -225,8 +331,11 @@ class CustomJWTBearer(AsyncJWTBearer):
225
331
  return None
226
332
  return request.user
227
333
  ```
334
+
228
335
  - Then add it to views.
336
+
229
337
  ```Python
338
+ # views.py
230
339
  from ninja import NinjAPI, Schema
231
340
  from ninja_aio.views import APIViewSet, APIView
232
341
  from ninja_aio.parsers import ORJSONParser
@@ -259,7 +368,7 @@ class SumView(APIView):
259
368
  auths = CustomJWTBearer()
260
369
 
261
370
  def views(self):
262
- @self.router.post(self.api_router_path, response={200: ExampleSchemaOut}, auth=self.auths)
371
+ @self.router.post("/", response={200: ExampleSchemaOut}, auth=self.auths)
263
372
  async def sum(request: HttpRequest, data: ExampleSchemaIn):
264
373
  return 200, {sum: data.n1 + data.n2}
265
374
 
@@ -268,6 +377,20 @@ FooAPI().add_views_to_route()
268
377
  SumView().add_views_to_route()
269
378
  ```
270
379
 
380
+ ## 📝 Pagination
381
+
382
+ - 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>**.
383
+
384
+ ```Python
385
+ # views.py
386
+
387
+ class FooAPI(APIViewSet):
388
+ model = Foo
389
+ api = api
390
+ pagination_class = CustomPaginationClass
391
+
392
+ ```
393
+
271
394
  ## 📌 Notes
272
395
  - Feel free to contribute and improve the program. 🛠️
273
396
 
@@ -5,24 +5,32 @@
5
5
  ## 📝 Instructions
6
6
 
7
7
  ### 📚 Prerequisites
8
+
8
9
  - Install Python from the [official website](https://www.python.org/) (latest version) and ensure it is added to the system Path and environment variables.
9
10
 
10
11
  ### 💻 Setup your environment
12
+
11
13
  - Create a virtual environment
12
14
  ```bash
13
15
  python -m venv .venv
14
16
  ```
17
+
15
18
  ### ✅ Activate it
19
+
16
20
  - If you are from linux activate it with
21
+
17
22
  ```bash
18
23
  . .venv/bin/activate
19
24
  ```
25
+
20
26
  - If you are from windows activate it with
27
+
21
28
  ```bash
22
29
  . .venv/Scripts/activate
23
30
  ```
24
31
 
25
32
  ### 📥 Install package
33
+
26
34
  ```bash
27
35
  pip install django-ninja-aio-crud
28
36
  ```
@@ -34,14 +42,17 @@ pip install django-ninja-aio-crud
34
42
  > and why not ... [Buy me a coffee](https://buymeacoffee.com/caspel26)
35
43
 
36
44
  ### ModelSerializer
45
+
37
46
  - You can serialize your models using ModelSerializer and made them inherit from it. In your models.py import ModelSerializer
38
47
  ```Python
48
+ # models.py
49
+ from django.db import models
39
50
  from ninja_aio.models import ModelSerializer
40
51
 
41
52
 
42
53
  class Foo(ModelSerializer):
43
- name = mdoels.CharField()
44
- bar = models.CharField()
54
+ name = models.CharField(max_length=30)
55
+ bar = models.CharField(max_length=30)
45
56
 
46
57
  class ReadSerializer:
47
58
  fields = ["id", "name", "bar"]
@@ -52,14 +63,18 @@ class Foo(ModelSerializer):
52
63
  class UpdateSerializer:
53
64
  fields = ["name", "bar"]
54
65
  ```
66
+
55
67
  - 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).
68
+
56
69
  ```Python
70
+ # models.py
71
+ from django.db import models
57
72
  from ninja_aio.models import ModelSerializer
58
73
 
59
74
 
60
75
  class Foo(ModelSerializer):
61
- name = mdoels.CharField()
62
- bar = models.CharField()
76
+ name = models.CharField(max_length=30)
77
+ bar = models.CharField(max_length=30)
63
78
  active = models.BooleanField(default=False)
64
79
 
65
80
  class ReadSerializer:
@@ -83,12 +98,15 @@ class Foo(ModelSerializer):
83
98
  self.active = True
84
99
  await self.asave()
85
100
  ```
86
- - 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.
87
101
 
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.
88
103
 
89
104
  ### APIViewSet
105
+
90
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
+
91
108
  ```Python
109
+ # views.py
92
110
  from ninja import NinjAPI
93
111
  from ninja_aio.views import APIViewSet
94
112
  from ninja_aio.parsers import ORJSONParser
@@ -106,8 +124,11 @@ class FooAPI(APIViewSet):
106
124
 
107
125
  FooAPI().add_views_to_route()
108
126
  ```
127
+
109
128
  - 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".
129
+
110
130
  ```Python
131
+ # views.py
111
132
  from ninja import NinjAPI, Schema
112
133
  from ninja_aio.views import APIViewSet
113
134
  from ninja_aio.parsers import ORJSONParser
@@ -132,7 +153,7 @@ class FooAPI(APIViewSet):
132
153
  api = api
133
154
 
134
155
  def views(self):
135
- @self.router.post("numbers-sum/", response={200: ExampleSchemaOut)
156
+ @self.router.post("numbers-sum/", response={200: ExampleSchemaOut})
136
157
  async def sum(request: HttpRequest, data: ExampleSchemaIn):
137
158
  return 200, {sum: data.n1 + data.n2}
138
159
 
@@ -141,8 +162,11 @@ FooAPI().add_views_to_route()
141
162
  ```
142
163
 
143
164
  ### APIView
165
+
144
166
  - View class to code generic views class based. In your views.py import APIView class.
167
+
145
168
  ```Python
169
+ # views.py
146
170
  from ninja import NinjAPI, Schema
147
171
  from ninja_aio.views import APIView
148
172
  from ninja_aio.parsers import ORJSONParser
@@ -166,7 +190,7 @@ class SumView(APIView):
166
190
  router_tag = "Sum"
167
191
 
168
192
  def views(self):
169
- @self.router.post(self.api_router_path, response={200: ExampleSchemaOut)
193
+ @self.router.post("/", response={200: ExampleSchemaOut})
170
194
  async def sum(request: HttpRequest, data: ExampleSchemaIn):
171
195
  return 200, {sum: data.n1 + data.n2}
172
196
 
@@ -174,9 +198,91 @@ class SumView(APIView):
174
198
  SumView().add_views_to_route()
175
199
  ```
176
200
 
201
+ ### 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.
206
+
207
+ - Define models:
208
+
209
+ ```Python
210
+ # models.py
211
+ class Bar(ModelSerializer):
212
+ name = models.CharField(max_length=30)
213
+ description = models.TextField(max_length=30)
214
+
215
+ # ReadSerializer with reverse OneToMany relation (foos)
216
+ class ReadSerializer:
217
+ fields = ["id", "name", "description", "foos"]
218
+
219
+ class CreateSerializer:
220
+ fields = ["name", "description"]
221
+
222
+ class UpdateSerializer:
223
+ fields = ["name", "description"]
224
+
225
+
226
+ class Foo(ModelSerializer):
227
+ name = models.CharField(max_length=30)
228
+ bar = models.ForeignKey(Bar, on_delete=models.CASCADE, related_name="foos")
229
+
230
+ class ReadSerializer:
231
+ fields = ["id", "name", "bar"]
232
+
233
+ class CreateSerializer:
234
+ fields = ["name", "bar"]
235
+
236
+ class UpdateSerializer:
237
+ fields = ["name"]
238
+ ```
239
+
240
+ - Define views:
241
+
242
+ ```Python
243
+ # views.py
244
+ from ninja import NinjAPI
245
+ from ninja_aio.views import APIViewSet
246
+ from ninja_aio.parsers import ORJSONParser
247
+ from ninja_aio.renders import ORJSONRender
248
+
249
+ from .models import Foo, Bar
250
+
251
+ api = NinjaAPI(renderer=ORJSONRenderer(), parser=ORJSONParser())
252
+
253
+
254
+ class FooAPI(APIViewSet):
255
+ model = Foo
256
+ api = api
257
+
258
+
259
+ class BarAPI(APIViewSet):
260
+ model = Bar
261
+ api = api
262
+
263
+
264
+ FooAPI().add_views_to_route()
265
+ BarAPI().add_views_to_route()
266
+ ```
267
+
268
+ - Now run your server and go to /docs url:
269
+
270
+ ### Docs
271
+
272
+ - Foo Schemas
273
+
274
+ ![Swagger UI](ninja_aio/docs/images/foo-swagger.png)
275
+
276
+ - Bar Schemas with reverse relation
277
+
278
+ ![Swagger UI](ninja_aio/docs/images/bar-swagger.png)
279
+
177
280
  ## 🔒 Authentication
281
+
178
282
  ### Jwt
283
+
179
284
  - 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.
285
+
180
286
  ```Python
181
287
  from ninja_aio.auth import AsyncJWTBearer
182
288
  from django.conf import settings
@@ -196,8 +302,11 @@ class CustomJWTBearer(AsyncJWTBearer):
196
302
  return None
197
303
  return request.user
198
304
  ```
305
+
199
306
  - Then add it to views.
307
+
200
308
  ```Python
309
+ # views.py
201
310
  from ninja import NinjAPI, Schema
202
311
  from ninja_aio.views import APIViewSet, APIView
203
312
  from ninja_aio.parsers import ORJSONParser
@@ -230,7 +339,7 @@ class SumView(APIView):
230
339
  auths = CustomJWTBearer()
231
340
 
232
341
  def views(self):
233
- @self.router.post(self.api_router_path, response={200: ExampleSchemaOut}, auth=self.auths)
342
+ @self.router.post("/", response={200: ExampleSchemaOut}, auth=self.auths)
234
343
  async def sum(request: HttpRequest, data: ExampleSchemaIn):
235
344
  return 200, {sum: data.n1 + data.n2}
236
345
 
@@ -239,5 +348,19 @@ FooAPI().add_views_to_route()
239
348
  SumView().add_views_to_route()
240
349
  ```
241
350
 
351
+ ## 📝 Pagination
352
+
353
+ - 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>**.
354
+
355
+ ```Python
356
+ # views.py
357
+
358
+ class FooAPI(APIViewSet):
359
+ model = Foo
360
+ api = api
361
+ pagination_class = CustomPaginationClass
362
+
363
+ ```
364
+
242
365
  ## 📌 Notes
243
366
  - Feel free to contribute and improve the program. 🛠️
@@ -1,3 +1,3 @@
1
1
  """ Django Ninja AIO CRUD - Rest Framework """
2
2
 
3
- __version__ = "0.1.2"
3
+ __version__ = "0.1.3"
@@ -204,18 +204,6 @@ class ModelSerializer(models.Model):
204
204
  return None
205
205
  return customs
206
206
 
207
- @classmethod
208
- def get_optional_fields(cls, s_type: type[S_TYPES]) -> list[str] | None:
209
- try:
210
- match s_type:
211
- case "create":
212
- optionals = cls.CreateSerializer.optionals
213
- case "update":
214
- optionals = cls.UpdateSerializer.optionals
215
- except AttributeError:
216
- return None
217
- return optionals
218
-
219
207
  @classmethod
220
208
  def generate_read_s(cls, depth: int = 1) -> Schema:
221
209
  fields, reverse_rels = cls.get_schema_out_data()
@@ -234,7 +222,6 @@ class ModelSerializer(models.Model):
234
222
  model=cls,
235
223
  name=f"{cls._meta.model_name}SchemaIn",
236
224
  fields=cls.CreateSerializer.fields,
237
- optional_fields=cls.get_optional_fields("create"),
238
225
  custom_fields=cls.get_custom_fields("create"),
239
226
  )
240
227
 
@@ -244,7 +231,6 @@ class ModelSerializer(models.Model):
244
231
  model=cls,
245
232
  name=f"{cls._meta.model_name}SchemaPatch",
246
233
  fields=cls.UpdateSerializer.fields,
247
- optional_fields=cls.get_optional_fields("update"),
248
234
  custom_fields=cls.get_custom_fields("update"),
249
235
  )
250
236
 
@@ -2,6 +2,7 @@ from typing import List
2
2
 
3
3
  from ninja import NinjaAPI, Router
4
4
  from ninja.constants import NOT_SET
5
+ from ninja.pagination import paginate, AsyncPaginationBase, PageNumberPagination
5
6
  from django.http import HttpRequest
6
7
 
7
8
  from .models import ModelSerializer
@@ -67,6 +68,7 @@ class APIViewSet:
67
68
  model: ModelSerializer
68
69
  api: NinjaAPI
69
70
  auths: list | None = NOT_SET
71
+ pagination_class: type[AsyncPaginationBase] = PageNumberPagination
70
72
 
71
73
  def __init__(self) -> None:
72
74
  self.router = Router(tags=[self.model._meta.model_name.capitalize()])
@@ -97,6 +99,7 @@ class APIViewSet:
97
99
  self.error_codes: GenericMessageSchema,
98
100
  },
99
101
  )
102
+ @paginate(self.pagination_class)
100
103
  async def list(request: HttpRequest):
101
104
  qs = await self.model.queryset_request(request)
102
105
  rels = self.model.get_reverse_relations()