django-ninja-aio-crud 0.11.4__py3-none-any.whl → 1.0.1__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.
@@ -1,527 +0,0 @@
1
- Metadata-Version: 2.4
2
- Name: django-ninja-aio-crud
3
- Version: 0.11.4
4
- Summary: Django Ninja AIO CRUD - Rest Framework
5
- Author: Giuseppe Casillo
6
- Requires-Python: >=3.10
7
- Description-Content-Type: text/markdown
8
- Classifier: Operating System :: OS Independent
9
- Classifier: Topic :: Internet
10
- Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
11
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
12
- Classifier: Topic :: Software Development :: Libraries
13
- Classifier: Topic :: Software Development
14
- Classifier: Environment :: Web Environment
15
- Classifier: Intended Audience :: Developers
16
- Classifier: License :: OSI Approved :: MIT License
17
- Classifier: Programming Language :: Python :: 3.10
18
- Classifier: Programming Language :: Python :: 3.11
19
- Classifier: Programming Language :: Python :: 3.12
20
- Classifier: Programming Language :: Python :: 3 :: Only
21
- Classifier: Framework :: Django
22
- Classifier: Framework :: AsyncIO
23
- Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
24
- Classifier: Topic :: Internet :: WWW/HTTP
25
- License-File: LICENSE
26
- Requires-Dist: django-ninja >=1.3.0
27
- Requires-Dist: joserfc >=1.0.0
28
- Requires-Dist: orjson >= 3.10.7
29
- Requires-Dist: coverage ; extra == "test"
30
- Project-URL: Documentation, https://caspel26.github.io/django-ninja-aio-crud/
31
- Project-URL: Repository, https://github.com/caspel26/django-ninja-aio-crud
32
- Provides-Extra: test
33
-
34
- # 🥷 django-ninja-aio-crud
35
- ![Test](https://github.com/caspel26/django-ninja-aio-crud/actions/workflows/coverage.yml/badge.svg)
36
- [![codecov](https://codecov.io/gh/caspel26/django-ninja-aio-crud/graph/badge.svg?token=DZ5WDT3S20)](https://codecov.io/gh/caspel26/django-ninja-aio-crud)
37
- [![PyPI - Version](https://img.shields.io/pypi/v/django-ninja-aio-crud?color=g&logo=pypi&logoColor=white)](https://pypi.org/project/django-ninja-aio-crud/)
38
- [![PyPI - License](https://img.shields.io/pypi/l/django-ninja-aio-crud)](https://github.com/caspel26/django-ninja-aio-crud/blob/main/LICENSE)
39
- [![Ruff](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/astral-sh/ruff/main/assets/badge/v2.json)](https://github.com/astral-sh/ruff)
40
- > [!NOTE]
41
- > Django ninja aio crud framework is based on **<a href="https://django-ninja.dev/">Django Ninja framework</a>**. It comes out with built-in views and models which are able to make automatic async CRUD operations and codes views class based making the developers' life easier and the code cleaner.
42
-
43
- ## 📝 Instructions
44
-
45
- ### 📚 Prerequisites
46
-
47
- - Install Python from the [official website](https://www.python.org/) (latest version) and ensure it is added to the system Path and environment variables.
48
-
49
- ### 💻 Setup your environment
50
-
51
- - Create a virtual environment
52
- ```bash
53
- python -m venv .venv
54
- ```
55
-
56
- ### ✅ Activate it
57
-
58
- - If you are from linux activate it with
59
-
60
- ```bash
61
- . .venv/bin/activate
62
- ```
63
-
64
- - If you are from windows activate it with
65
-
66
- ```bash
67
- . .venv/Scripts/activate
68
- ```
69
-
70
- ### 📥 Install package
71
-
72
- ```bash
73
- pip install django-ninja-aio-crud
74
- ```
75
-
76
- ## 🚀 Usage
77
-
78
- > [!TIP]
79
- > If you find **django ninja aio crud** useful, consider :star: this project
80
- > and why not ... [Buy me a coffee](https://buymeacoffee.com/caspel26)
81
-
82
- ### ModelSerializer
83
-
84
- - You can serialize your models using ModelSerializer and made them inherit from it. In your models.py import ModelSerializer
85
- ```python
86
- # models.py
87
- from django.db import models
88
- from ninja_aio.models import ModelSerializer
89
-
90
-
91
- class Foo(ModelSerializer):
92
- name = models.CharField(max_length=30)
93
- bar = models.CharField(max_length=30)
94
-
95
- class ReadSerializer:
96
- fields = ["id", "name", "bar"]
97
-
98
- class CreateSerializer:
99
- fields = ["name", "bar"]
100
-
101
- class UpdateSerializer:
102
- fields = ["name", "bar"]
103
- ```
104
-
105
- - 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.
106
-
107
- ```python
108
- # models.py
109
- from django.db import models
110
- from ninja_aio.models import ModelSerializer
111
-
112
-
113
- class Foo(ModelSerializer):
114
- name = models.CharField(max_length=30)
115
- bar = models.CharField(max_length=30)
116
- active = models.BooleanField(default=False)
117
-
118
- class ReadSerializer:
119
- fields = ["id", "name", "bar"]
120
-
121
- class CreateSerializer:
122
- customs = [("force_activation", bool, False)]
123
- fields = ["name", "bar"]
124
-
125
- class UpdateSerializer:
126
- fields = ["name", "bar"]
127
-
128
- async def custom_actions(self, payload: dict[str, Any]):
129
- if not payload.get("force_activation"):
130
- return
131
- setattr(self, "force_activation", True)
132
-
133
- async def post_create(self) -> None:
134
- if not hasattr(self, "force_activation") or not getattr(self, "force_activation"):
135
- return
136
- self.active = True
137
- await self.asave()
138
- ```
139
-
140
- - 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.
141
-
142
- - You can also define optional fields for you Create and Update serializers. To declare an optional fields you have to give the field type too.
143
- ```python
144
- # models.py
145
- from django.db import models
146
- from ninja_aio.models import ModelSerializer
147
-
148
-
149
- class Foo(ModelSerializer):
150
- name = models.CharField(max_length=30)
151
- bar = models.CharField(max_length=30, default="")
152
- active = models.BooleanField(default=False)
153
-
154
- class ReadSerializer:
155
- fields = ["id", "name", "bar"]
156
-
157
- class CreateSerializer:
158
- fields = ["name"]
159
- optionals = [("bar", str), ("active", bool)]
160
-
161
- class UpdateSerializer:
162
- optionals = [("bar", str), ("active", bool)]
163
- ```
164
-
165
- - Instead of declaring your fields maybe you want to exclude some of them. Declaring "excludes" attribute into serializers will exclude the given fields. (You can declare only one between "fields" and "excludes").
166
-
167
- ```python
168
- # models.py
169
- from django.db import models
170
- from ninja_aio.models import ModelSerializer
171
-
172
-
173
- class Foo(ModelSerializer):
174
- name = models.CharField(max_length=30)
175
- bar = models.CharField(max_length=30, default="")
176
- active = models.BooleanField(default=False)
177
-
178
- class ReadSerializer:
179
- excludes = ["bar"]
180
-
181
- class CreateSerializer:
182
- fields = ["name"]
183
- optionals = [("bar", str), ("active", bool)]
184
-
185
- class UpdateSerializer:
186
- excludes = ["id", "name"]
187
- optionals = [("bar", str), ("active", bool)]
188
- ```
189
- - If you want to add into ReadSerializer model properties you must define them as customs.
190
- ```python
191
- # models.py
192
- from django.db import models
193
- from ninja_aio.models import ModelSerializer
194
-
195
-
196
- class Foo(ModelSerializer):
197
- name = models.CharField(max_length=30)
198
- bar = models.CharField(max_length=30, default="")
199
- active = models.BooleanField(default=False)
200
-
201
- @property
202
- def full_name(self):
203
- return f"{self.name} example_full_name"
204
-
205
- class ReadSerializer:
206
- excludes = ["bar"]
207
- customs = [("full_name", str, "")]
208
-
209
- class CreateSerializer:
210
- fields = ["name"]
211
- optionals = [("bar", str), ("active", bool)]
212
-
213
- class UpdateSerializer:
214
- excludes = ["id", "name"]
215
- optionals = [("bar", str), ("active", bool)]
216
- ```
217
- - ModelSerializer comes out also with methods executed on object save and delete, them are:
218
-
219
- 1. on_create_before_save: code executed on object creation but before saving;
220
- 1. on_create_after_save: code executed on object creation but after saving;
221
- 1. before_save: code executed on every save but before saving;
222
- 1. after_save: code executed on every save but after saving;
223
- 1. on_delete: code executed after object delete;
224
-
225
-
226
- ### APIViewSet
227
-
228
- - 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.
229
-
230
- ```python
231
- # views.py
232
- from ninja_aio import NinjaAIO
233
- from ninja_aio.views import APIViewSet
234
-
235
- from .models import Foo
236
-
237
- api = NinjaAIO()
238
-
239
-
240
- class FooAPI(APIViewSet):
241
- model = Foo
242
- api = api
243
-
244
-
245
- FooAPI().add_views_to_route()
246
- ```
247
-
248
- - 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".
249
-
250
- ```python
251
- # views.py
252
- from ninja import Schema
253
- from ninja_aio import NinjaAIO
254
- from ninja_aio.views import APIViewSet
255
-
256
- from .models import Foo
257
-
258
- api = NinjaAIO()
259
-
260
-
261
- class ExampleSchemaOut(Schema):
262
- sum: float
263
-
264
-
265
- class ExampleSchemaIn(Schema):
266
- n1: float
267
- n2: float
268
-
269
-
270
- class FooAPI(APIViewSet):
271
- model = Foo
272
- api = api
273
-
274
- def views(self):
275
- @self.router.post("numbers-sum/", response={200: ExampleSchemaOut})
276
- async def sum(request: HttpRequest, data: ExampleSchemaIn):
277
- return 200, {sum: data.n1 + data.n2}
278
-
279
-
280
- FooAPI().add_views_to_route()
281
- ```
282
-
283
- - You can also choose to disable any operation from crud by declaring "disbale" attribute. You can give "all" to disable every crud operation except for additional views.
284
-
285
- > [!TIP]
286
- > You can exclude by default an endpoint without declaring the serializer.
287
- > For example if you don't want to give update method to a CRUD just do not declare UpdateSerializer into model.
288
-
289
- ```python
290
- # views.py
291
- from ninja_aio import NinjaAIO
292
- from ninja_aio.views import APIViewSet
293
-
294
- from .models import Foo
295
-
296
- api = NinjaAIO()
297
-
298
-
299
- class FooAPI(APIViewSet):
300
- model = Foo
301
- api = api
302
- disable = ["retrieve", "update"]
303
-
304
-
305
- FooAPI().add_views_to_route()
306
- ```
307
- For the list endpoint you can also set query params and handle them. They will be also visible into swagger.
308
-
309
- ```python
310
- # views.py
311
- from ninja_aio import NinjaAIO
312
- from ninja_aio.views import APIViewSet
313
-
314
- from .models import Foo
315
-
316
- api = NinjaAIO()
317
-
318
-
319
- class FooAPI(APIViewSet):
320
- model = Foo
321
- api = api
322
- query_params = {"name": (str, None), "active": (bool, None)}
323
-
324
- async def query_params_handler(self, queryset, filters):
325
- return queryset.filter(**{k: v for k, v in filters.items() if v is not None})
326
-
327
-
328
- FooAPI().add_views_to_route()
329
- ```
330
-
331
- ### APIView
332
-
333
- - View class to code generic views class based. In your views.py import APIView class.
334
-
335
- ```python
336
- # views.py
337
- from ninja import Schema
338
- from ninja_aio import NinjaAIO
339
- from ninja_aio.views import APIView
340
-
341
- api = NinjaAIO()
342
-
343
-
344
- class ExampleSchemaOut(Schema):
345
- sum: float
346
-
347
-
348
- class ExampleSchemaIn(Schema):
349
- n1: float
350
- n2: float
351
-
352
-
353
- class SumView(APIView):
354
- api = api
355
- api_router_path = "numbers-sum/"
356
- router_tag = "Sum"
357
-
358
- def views(self):
359
- @self.router.post("/", response={200: ExampleSchemaOut})
360
- async def sum(request: HttpRequest, data: ExampleSchemaIn):
361
- return 200, {sum: data.n1 + data.n2}
362
-
363
-
364
- SumView().add_views_to_route()
365
- ```
366
-
367
- ### Relations
368
- - You can also set ForeignKey, OneToOne and ManyToMany relations into serialization(reverse relations are supported too). Django ninja aio crud will serialize every of these relation automatically.
369
-
370
- - Define models:
371
-
372
- ```python
373
- # models.py
374
- class Bar(ModelSerializer):
375
- name = models.CharField(max_length=30)
376
- description = models.TextField(max_length=30)
377
-
378
- # ReadSerializer with reverse OneToMany relation (foos)
379
- class ReadSerializer:
380
- fields = ["id", "name", "description", "foos"]
381
-
382
- class CreateSerializer:
383
- fields = ["name", "description"]
384
-
385
- class UpdateSerializer:
386
- fields = ["name", "description"]
387
-
388
-
389
- class Foo(ModelSerializer):
390
- name = models.CharField(max_length=30)
391
- bar = models.ForeignKey(Bar, on_delete=models.CASCADE, related_name="foos")
392
-
393
- class ReadSerializer:
394
- fields = ["id", "name", "bar"]
395
-
396
- class CreateSerializer:
397
- fields = ["name", "bar"]
398
-
399
- class UpdateSerializer:
400
- fields = ["name"]
401
- ```
402
-
403
- - Define views:
404
-
405
- ```python
406
- # views.py
407
- from ninja_aio import NinjaAIO
408
- from ninja_aio.views import APIViewSet
409
-
410
- from .models import Foo, Bar
411
-
412
- api = NinjaAIO()
413
-
414
-
415
- class FooAPI(APIViewSet):
416
- model = Foo
417
- api = api
418
-
419
-
420
- class BarAPI(APIViewSet):
421
- model = Bar
422
- api = api
423
-
424
-
425
- FooAPI().add_views_to_route()
426
- BarAPI().add_views_to_route()
427
- ```
428
-
429
- - Now run your server and go to /docs url:
430
-
431
- ### Docs
432
-
433
- - Foo Schemas
434
-
435
- ![Swagger UI](docs/images/foo-swagger.png)
436
-
437
- - Bar Schemas with reverse relation
438
-
439
- ![Swagger UI](docs/images/bar-swagger.png)
440
-
441
- ## 🔒 Authentication
442
-
443
- ### Jwt
444
-
445
- - 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.
446
-
447
- ```python
448
- from ninja_aio.auth import AsyncJWTBearer
449
- from django.conf import settings
450
- from django.http import HttpRequest
451
-
452
- from .models import Foo
453
-
454
-
455
- class CustomJWTBearer(AsyncJWTBearer):
456
- jwt_public = settings.JWT_PUBLIC
457
- claims = {"foo_id": {"essential": True}}
458
-
459
- async def auth_handler(self, request: HttpRequest):
460
- try:
461
- request.user = await Foo.objects.aget(id=self.dcd.claims["foo_id"])
462
- except Foo.DoesNotExist:
463
- return None
464
- return request.user
465
- ```
466
-
467
- - Then add it to views.
468
-
469
- ```python
470
- # views.py
471
- from ninja import Schema
472
- from ninja_aio import NinjaAIO
473
- from ninja_aio.views import APIViewSet, APIView
474
-
475
- from .models import Foo
476
-
477
- api = NinjaAIO()
478
-
479
-
480
- class FooAPI(APIViewSet):
481
- model = Foo
482
- api = api
483
- auth = CustomJWTBearer()
484
-
485
-
486
- class ExampleSchemaOut(Schema):
487
- sum: float
488
-
489
-
490
- class ExampleSchemaIn(Schema):
491
- n1: float
492
- n2: float
493
-
494
-
495
- class SumView(APIView):
496
- api = api
497
- api_router_path = "numbers-sum/"
498
- router_tag = "Sum"
499
- auth = CustomJWTBearer()
500
-
501
- def views(self):
502
- @self.router.post("/", response={200: ExampleSchemaOut}, auth=self.auth)
503
- async def sum(request: HttpRequest, data: ExampleSchemaIn):
504
- return 200, {sum: data.n1 + data.n2}
505
-
506
-
507
- FooAPI().add_views_to_route()
508
- SumView().add_views_to_route()
509
- ```
510
-
511
- ## 📝 Pagination
512
-
513
- - 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>**.
514
-
515
- ```python
516
- # views.py
517
-
518
- class FooAPI(APIViewSet):
519
- model = Foo
520
- api = api
521
- pagination_class = CustomPaginationClass
522
-
523
- ```
524
-
525
- ## 📌 Notes
526
- - Feel free to contribute and improve the program. 🛠️
527
-