django-ninja-aio-crud 2.3.0__tar.gz → 2.3.2__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.
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/.github/workflows/docs.yml +2 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/PKG-INFO +20 -11
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/README.md +17 -8
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/views/decorators.md +1 -2
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/index.md +34 -43
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/__init__.py +1 -1
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/renders.py +2 -1
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/pyproject.toml +2 -2
- django_ninja_aio_crud-2.3.0/examples/ex_1/models.py +0 -35
- django_ninja_aio_crud-2.3.0/examples/ex_1/urls.py +0 -11
- django_ninja_aio_crud-2.3.0/examples/ex_1/views.py +0 -16
- django_ninja_aio_crud-2.3.0/examples/ex_2/auth.py +0 -33
- django_ninja_aio_crud-2.3.0/examples/ex_2/models.py +0 -62
- django_ninja_aio_crud-2.3.0/examples/ex_2/urls.py +0 -11
- django_ninja_aio_crud-2.3.0/examples/ex_2/views.py +0 -26
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/.github/dependabot.yml +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/.github/workflows/coverage.yml +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/.github/workflows/publish.yml +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/.gitignore +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/.pre-commit-config.yaml +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/LICENSE +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/CNAME +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/authentication.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/models/model_serializer.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/models/model_util.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/pagination.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/renderers/orjson_renderer.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/views/api_view.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/views/api_view_set.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/views/mixins.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/auth.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/contributing.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/extra.css +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/images/index/foo-index-create-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/images/index/foo-index-delete-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/images/index/foo-index-list-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/images/index/foo-index-retrieve-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/images/index/foo-index-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/images/index/foo-index-update-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/installation.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/quick_start.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/images/bar-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/images/favicon.ico +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/images/foo-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/images/logo.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/images/model_util/foo-reverse-relations-swagger.png +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/release_notes.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/requirements.txt +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/tutorial/authentication.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/tutorial/crud.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/tutorial/filtering.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/tutorial/model.md +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/main.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/mkdocs.yml +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/api.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/auth.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/decorators/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/decorators/operations.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/decorators/views.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/exceptions.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/factory/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/factory/operations.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/helpers/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/helpers/api.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/helpers/query.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/models.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/parsers.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/schemas/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/schemas/api.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/schemas/generics.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/schemas/helpers.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/types.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/views/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/views/api.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/views/mixins.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/requirements.dev.txt +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/run-local-coverage.sh +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/core/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/core/test_decorators.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/core/test_exceptions_api.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/core/test_renderer_parser.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/generics/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/generics/literals.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/generics/models.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/generics/request.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/generics/views.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/helpers/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/helpers/test_many_to_many_api.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/models/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/models/test_model_util.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/models/test_models_extra.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_app/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_app/models.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_app/schema.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_app/views.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_auth.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_decorators.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_exceptions.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_query_util.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/test_settings.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/views/__init__.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/views/test_views.py +0 -0
- {django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/views/test_viewset.py +0 -0
|
@@ -14,6 +14,7 @@ on:
|
|
|
14
14
|
- "1.0"
|
|
15
15
|
- "2.0"
|
|
16
16
|
- "2.2"
|
|
17
|
+
- "2.3"
|
|
17
18
|
make_latest:
|
|
18
19
|
description: 'Set as "latest" and default?'
|
|
19
20
|
type: boolean
|
|
@@ -31,6 +32,7 @@ on:
|
|
|
31
32
|
- "1.0"
|
|
32
33
|
- "2.0"
|
|
33
34
|
- "2.2"
|
|
35
|
+
- "2.3"
|
|
34
36
|
delete_confirm:
|
|
35
37
|
description: 'Confirm deletion of the selected version'
|
|
36
38
|
type: boolean
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: django-ninja-aio-crud
|
|
3
|
-
Version: 2.3.
|
|
3
|
+
Version: 2.3.2
|
|
4
4
|
Summary: Django Ninja AIO CRUD - Rest Framework
|
|
5
5
|
Author: Giuseppe Casillo
|
|
6
|
-
Requires-Python: >=3.10,
|
|
6
|
+
Requires-Python: >=3.10, <3.15
|
|
7
7
|
Description-Content-Type: text/markdown
|
|
8
8
|
Classifier: Operating System :: OS Independent
|
|
9
9
|
Classifier: Topic :: Internet
|
|
@@ -25,7 +25,7 @@ Classifier: Framework :: AsyncIO
|
|
|
25
25
|
Classifier: Topic :: Internet :: WWW/HTTP :: HTTP Servers
|
|
26
26
|
Classifier: Topic :: Internet :: WWW/HTTP
|
|
27
27
|
License-File: LICENSE
|
|
28
|
-
Requires-Dist: django-ninja >=1.3.0,
|
|
28
|
+
Requires-Dist: django-ninja >=1.3.0, <1.6
|
|
29
29
|
Requires-Dist: joserfc >=1.0.0, <= 1.4.1
|
|
30
30
|
Requires-Dist: orjson >= 3.10.7, <= 3.11.5
|
|
31
31
|
Requires-Dist: coverage ; extra == "test"
|
|
@@ -74,6 +74,7 @@ Add to your project’s dependencies and ensure Django Ninja is installed.
|
|
|
74
74
|
## 🚀 Quick Start
|
|
75
75
|
|
|
76
76
|
models.py
|
|
77
|
+
|
|
77
78
|
```python
|
|
78
79
|
from django.db import models
|
|
79
80
|
from ninja_aio.models import ModelSerializer
|
|
@@ -93,6 +94,7 @@ class Book(ModelSerializer):
|
|
|
93
94
|
```
|
|
94
95
|
|
|
95
96
|
views.py
|
|
97
|
+
|
|
96
98
|
```python
|
|
97
99
|
from ninja_aio import NinjaAIO
|
|
98
100
|
from ninja_aio.views import APIViewSet
|
|
@@ -126,6 +128,7 @@ class BookViewSet(APIViewSet):
|
|
|
126
128
|
```
|
|
127
129
|
|
|
128
130
|
Request:
|
|
131
|
+
|
|
129
132
|
```
|
|
130
133
|
GET /book/?published=true&title=python
|
|
131
134
|
```
|
|
@@ -168,6 +171,7 @@ class ArticleViewSet(APIViewSet):
|
|
|
168
171
|
```
|
|
169
172
|
|
|
170
173
|
Endpoints:
|
|
174
|
+
|
|
171
175
|
- `GET /article/{pk}/tag?name=dev`
|
|
172
176
|
- `POST /article/{pk}/tag/` body: `{"add":[1,2],"remove":[3]}`
|
|
173
177
|
|
|
@@ -202,6 +206,7 @@ class SecureBookViewSet(APIViewSet):
|
|
|
202
206
|
## 📑 Lifecycle Hooks (ModelSerializer)
|
|
203
207
|
|
|
204
208
|
Available on every save/delete:
|
|
209
|
+
|
|
205
210
|
- `on_create_before_save`
|
|
206
211
|
- `on_create_after_save`
|
|
207
212
|
- `before_save`
|
|
@@ -250,9 +255,8 @@ class LargePagination(PageNumberPagination):
|
|
|
250
255
|
page_size = 50
|
|
251
256
|
max_page_size = 200
|
|
252
257
|
|
|
258
|
+
@api.viewset(Book)
|
|
253
259
|
class BookViewSet(APIViewSet):
|
|
254
|
-
model = Book
|
|
255
|
-
api = api
|
|
256
260
|
pagination_class = LargePagination
|
|
257
261
|
```
|
|
258
262
|
|
|
@@ -261,6 +265,7 @@ class BookViewSet(APIViewSet):
|
|
|
261
265
|
## 🛠 Project Structure & Docs
|
|
262
266
|
|
|
263
267
|
Documentation (MkDocs + Material):
|
|
268
|
+
|
|
264
269
|
```
|
|
265
270
|
docs/
|
|
266
271
|
getting_started/
|
|
@@ -273,17 +278,19 @@ docs/
|
|
|
273
278
|
```
|
|
274
279
|
|
|
275
280
|
Browse full reference:
|
|
281
|
+
|
|
276
282
|
- APIViewSet: `docs/api/views/api_view_set.md`
|
|
277
283
|
- APIView: `docs/api/views/api_view.md`
|
|
278
284
|
- ModelSerializer: `docs/api/models/model_serializer.md`
|
|
279
285
|
- Authentication: `docs/api/authentication.md`
|
|
280
|
-
-
|
|
286
|
+
- Example repository: https://github.com/caspel26/ninja-aio-blog-example
|
|
281
287
|
|
|
282
288
|
---
|
|
283
289
|
|
|
284
290
|
## 🧪 Tests
|
|
285
291
|
|
|
286
292
|
Use Django test runner + async ORM patterns. Example async pattern:
|
|
293
|
+
|
|
287
294
|
```python
|
|
288
295
|
obj = await Book.objects.acreate(title="T1", published=True)
|
|
289
296
|
count = await Book.objects.acount()
|
|
@@ -323,6 +330,7 @@ class ReadOnlyBookViewSet(APIViewSet):
|
|
|
323
330
|
## ⭐ Support
|
|
324
331
|
|
|
325
332
|
Star the repo or donate:
|
|
333
|
+
|
|
326
334
|
- [Buy me a coffee](https://buymeacoffee.com/caspel26)
|
|
327
335
|
|
|
328
336
|
---
|
|
@@ -335,11 +343,12 @@ MIT License. See [LICENSE](LICENSE).
|
|
|
335
343
|
|
|
336
344
|
## 🔗 Quick Links
|
|
337
345
|
|
|
338
|
-
| Item
|
|
339
|
-
|
|
340
|
-
| PyPI
|
|
341
|
-
| Docs
|
|
342
|
-
| Issues
|
|
346
|
+
| Item | Link |
|
|
347
|
+
| ------- | -------------------------------------------------------- |
|
|
348
|
+
| PyPI | https://pypi.org/project/django-ninja-aio-crud/ |
|
|
349
|
+
| Docs | https://django-ninja-aio.com |
|
|
350
|
+
| Issues | https://github.com/caspel26/django-ninja-aio-crud/issues |
|
|
351
|
+
| Example | https://github.com/caspel26/ninja-aio-blog-example |
|
|
343
352
|
|
|
344
353
|
---
|
|
345
354
|
|
|
@@ -39,6 +39,7 @@ Add to your project’s dependencies and ensure Django Ninja is installed.
|
|
|
39
39
|
## 🚀 Quick Start
|
|
40
40
|
|
|
41
41
|
models.py
|
|
42
|
+
|
|
42
43
|
```python
|
|
43
44
|
from django.db import models
|
|
44
45
|
from ninja_aio.models import ModelSerializer
|
|
@@ -58,6 +59,7 @@ class Book(ModelSerializer):
|
|
|
58
59
|
```
|
|
59
60
|
|
|
60
61
|
views.py
|
|
62
|
+
|
|
61
63
|
```python
|
|
62
64
|
from ninja_aio import NinjaAIO
|
|
63
65
|
from ninja_aio.views import APIViewSet
|
|
@@ -91,6 +93,7 @@ class BookViewSet(APIViewSet):
|
|
|
91
93
|
```
|
|
92
94
|
|
|
93
95
|
Request:
|
|
96
|
+
|
|
94
97
|
```
|
|
95
98
|
GET /book/?published=true&title=python
|
|
96
99
|
```
|
|
@@ -133,6 +136,7 @@ class ArticleViewSet(APIViewSet):
|
|
|
133
136
|
```
|
|
134
137
|
|
|
135
138
|
Endpoints:
|
|
139
|
+
|
|
136
140
|
- `GET /article/{pk}/tag?name=dev`
|
|
137
141
|
- `POST /article/{pk}/tag/` body: `{"add":[1,2],"remove":[3]}`
|
|
138
142
|
|
|
@@ -167,6 +171,7 @@ class SecureBookViewSet(APIViewSet):
|
|
|
167
171
|
## 📑 Lifecycle Hooks (ModelSerializer)
|
|
168
172
|
|
|
169
173
|
Available on every save/delete:
|
|
174
|
+
|
|
170
175
|
- `on_create_before_save`
|
|
171
176
|
- `on_create_after_save`
|
|
172
177
|
- `before_save`
|
|
@@ -215,9 +220,8 @@ class LargePagination(PageNumberPagination):
|
|
|
215
220
|
page_size = 50
|
|
216
221
|
max_page_size = 200
|
|
217
222
|
|
|
223
|
+
@api.viewset(Book)
|
|
218
224
|
class BookViewSet(APIViewSet):
|
|
219
|
-
model = Book
|
|
220
|
-
api = api
|
|
221
225
|
pagination_class = LargePagination
|
|
222
226
|
```
|
|
223
227
|
|
|
@@ -226,6 +230,7 @@ class BookViewSet(APIViewSet):
|
|
|
226
230
|
## 🛠 Project Structure & Docs
|
|
227
231
|
|
|
228
232
|
Documentation (MkDocs + Material):
|
|
233
|
+
|
|
229
234
|
```
|
|
230
235
|
docs/
|
|
231
236
|
getting_started/
|
|
@@ -238,17 +243,19 @@ docs/
|
|
|
238
243
|
```
|
|
239
244
|
|
|
240
245
|
Browse full reference:
|
|
246
|
+
|
|
241
247
|
- APIViewSet: `docs/api/views/api_view_set.md`
|
|
242
248
|
- APIView: `docs/api/views/api_view.md`
|
|
243
249
|
- ModelSerializer: `docs/api/models/model_serializer.md`
|
|
244
250
|
- Authentication: `docs/api/authentication.md`
|
|
245
|
-
-
|
|
251
|
+
- Example repository: https://github.com/caspel26/ninja-aio-blog-example
|
|
246
252
|
|
|
247
253
|
---
|
|
248
254
|
|
|
249
255
|
## 🧪 Tests
|
|
250
256
|
|
|
251
257
|
Use Django test runner + async ORM patterns. Example async pattern:
|
|
258
|
+
|
|
252
259
|
```python
|
|
253
260
|
obj = await Book.objects.acreate(title="T1", published=True)
|
|
254
261
|
count = await Book.objects.acount()
|
|
@@ -288,6 +295,7 @@ class ReadOnlyBookViewSet(APIViewSet):
|
|
|
288
295
|
## ⭐ Support
|
|
289
296
|
|
|
290
297
|
Star the repo or donate:
|
|
298
|
+
|
|
291
299
|
- [Buy me a coffee](https://buymeacoffee.com/caspel26)
|
|
292
300
|
|
|
293
301
|
---
|
|
@@ -300,10 +308,11 @@ MIT License. See [LICENSE](LICENSE).
|
|
|
300
308
|
|
|
301
309
|
## 🔗 Quick Links
|
|
302
310
|
|
|
303
|
-
| Item
|
|
304
|
-
|
|
305
|
-
| PyPI
|
|
306
|
-
| Docs
|
|
307
|
-
| Issues
|
|
311
|
+
| Item | Link |
|
|
312
|
+
| ------- | -------------------------------------------------------- |
|
|
313
|
+
| PyPI | https://pypi.org/project/django-ninja-aio-crud/ |
|
|
314
|
+
| Docs | https://django-ninja-aio.com |
|
|
315
|
+
| Issues | https://github.com/caspel26/django-ninja-aio-crud/issues |
|
|
316
|
+
| Example | https://github.com/caspel26/ninja-aio-blog-example |
|
|
308
317
|
|
|
309
318
|
---
|
|
@@ -50,9 +50,8 @@ Attach decorators to CRUD operations without redefining views:
|
|
|
50
50
|
```python
|
|
51
51
|
from ninja_aio.schemas.helpers import DecoratorsSchema
|
|
52
52
|
|
|
53
|
+
@api.viewset(MyModel)
|
|
53
54
|
class MyViewSet(APIViewSet):
|
|
54
|
-
api = api
|
|
55
|
-
model = MyModel
|
|
56
55
|
extra_decorators = DecoratorsSchema(
|
|
57
56
|
list=[require_auth, cache_page(30)],
|
|
58
57
|
retrieve=[require_auth],
|
|
@@ -33,55 +33,54 @@ Traditional Django REST development requires:
|
|
|
33
33
|
**Django Ninja Aio CRUD** eliminates this complexity:
|
|
34
34
|
|
|
35
35
|
=== "Traditional Approach"
|
|
36
|
-
```python
|
|
36
|
+
```python
|
|
37
37
|
# schema.py
|
|
38
38
|
class UserSchemaOut(ModelSchema)
|
|
39
39
|
class Meta:
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
model = User
|
|
41
|
+
fields = ['id', 'username', 'email']
|
|
42
|
+
|
|
43
43
|
class UserSchemaIn(ModelSchema):
|
|
44
44
|
class Meta:
|
|
45
45
|
model = User
|
|
46
46
|
fields = ['username', 'email', 'password']
|
|
47
|
-
|
|
47
|
+
|
|
48
48
|
# views.py
|
|
49
49
|
@api.get("/users", response={200: list[UserSchemaOut]})
|
|
50
50
|
async def list_users(request):
|
|
51
51
|
return [user async for user in User.objects.select_related().all()]
|
|
52
|
-
|
|
52
|
+
|
|
53
53
|
@api.post("/users/", response={201: UserSchemaOut})
|
|
54
54
|
async def create_user(request, data: UserSchemaIn):
|
|
55
55
|
user = await User.objects.select_related().acreate(**data.model_dump())
|
|
56
56
|
return 201, user
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
|
|
59
59
|
# ... more views for retrieve, update, delete
|
|
60
60
|
```
|
|
61
61
|
|
|
62
62
|
=== "Django Ninja Aio CRUD"
|
|
63
|
-
```python
|
|
63
|
+
```python
|
|
64
64
|
# models.py
|
|
65
65
|
class User(ModelSerializer):
|
|
66
66
|
username = models.CharField(max_length=150)
|
|
67
67
|
email = models.EmailField()
|
|
68
68
|
password = models.CharField(max_length=128)
|
|
69
|
-
|
|
69
|
+
|
|
70
70
|
class ReadSerializer:
|
|
71
71
|
fields = ["id", "username", "email"]
|
|
72
|
-
|
|
72
|
+
|
|
73
73
|
class CreateSerializer:
|
|
74
74
|
fields = ["username", "email", "password"]
|
|
75
|
-
|
|
75
|
+
|
|
76
76
|
class UpdateSerializer:
|
|
77
77
|
optionals = [("email", str)]
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
# views.py
|
|
80
|
+
@api.viewset(User)
|
|
80
81
|
class UserViewSet(APIViewSet):
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
UserViewSet().add_views_to_route()
|
|
82
|
+
pass
|
|
83
|
+
|
|
85
84
|
# Done! List, Create, Retrieve, Update, Delete endpoints ready
|
|
86
85
|
```
|
|
87
86
|
|
|
@@ -213,19 +212,18 @@ from .models import Author, Category, Article, Tag
|
|
|
213
212
|
api = NinjaAIO(title="Blog API", version="1.0.0")
|
|
214
213
|
|
|
215
214
|
|
|
215
|
+
@api.viewset(Author)
|
|
216
216
|
class AuthorViewSet(APIViewSet):
|
|
217
|
-
|
|
218
|
-
api = api
|
|
217
|
+
pass
|
|
219
218
|
|
|
220
219
|
|
|
220
|
+
@api.viewset(Category)
|
|
221
221
|
class CategoryViewSet(APIViewSet):
|
|
222
|
-
|
|
223
|
-
api = api
|
|
222
|
+
pass
|
|
224
223
|
|
|
225
224
|
|
|
225
|
+
@api.viewset(Article)
|
|
226
226
|
class ArticleViewSet(APIViewSet):
|
|
227
|
-
model = Article
|
|
228
|
-
api = api
|
|
229
227
|
query_params = {
|
|
230
228
|
"is_published": (bool, None),
|
|
231
229
|
"category": (int, None),
|
|
@@ -242,16 +240,9 @@ class ArticleViewSet(APIViewSet):
|
|
|
242
240
|
return queryset
|
|
243
241
|
|
|
244
242
|
|
|
243
|
+
@api.viewset(Tag)
|
|
245
244
|
class TagViewSet(APIViewSet):
|
|
246
|
-
|
|
247
|
-
api = api
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
# Register all views
|
|
251
|
-
AuthorViewSet().add_views_to_route()
|
|
252
|
-
CategoryViewSet().add_views_to_route()
|
|
253
|
-
ArticleViewSet().add_views_to_route()
|
|
254
|
-
TagViewSet().add_views_to_route()
|
|
245
|
+
pass
|
|
255
246
|
```
|
|
256
247
|
|
|
257
248
|
This creates a complete blog API with:
|
|
@@ -287,9 +278,9 @@ class User(ModelSerializer):
|
|
|
287
278
|
Automatically generates complete CRUD endpoints:
|
|
288
279
|
|
|
289
280
|
```python
|
|
281
|
+
@api.viewset(User)
|
|
290
282
|
class UserViewSet(APIViewSet):
|
|
291
|
-
|
|
292
|
-
api = api
|
|
283
|
+
pass
|
|
293
284
|
# Generates: List, Create, Retrieve, Update, Delete
|
|
294
285
|
```
|
|
295
286
|
|
|
@@ -298,17 +289,16 @@ class UserViewSet(APIViewSet):
|
|
|
298
289
|
Extend with custom endpoints:
|
|
299
290
|
|
|
300
291
|
```python
|
|
292
|
+
from ninja_aio.decorators import api_post
|
|
293
|
+
|
|
294
|
+
@api.viewset(User)
|
|
301
295
|
class UserViewSet(APIViewSet):
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
user = await User.objects.aget(pk=pk)
|
|
309
|
-
user.is_active = True
|
|
310
|
-
await user.asave()
|
|
311
|
-
return {"message": "User activated"}
|
|
296
|
+
@api_post("/{pk}/activate")
|
|
297
|
+
async def activate(self, request, pk: int):
|
|
298
|
+
user = await User.objects.aget(pk=pk)
|
|
299
|
+
user.is_active = True
|
|
300
|
+
await user.asave()
|
|
301
|
+
return {"message": "User activated"}
|
|
312
302
|
```
|
|
313
303
|
|
|
314
304
|
## 📄 License
|
|
@@ -327,6 +317,7 @@ If you find Django Ninja Aio CRUD useful, consider supporting the project:
|
|
|
327
317
|
- **GitHub:** [https://github.com/caspel26/django-ninja-aio-crud](https://github.com/caspel26/django-ninja-aio-crud)
|
|
328
318
|
- **PyPI:** [https://pypi.org/project/django-ninja-aio-crud/](https://pypi.org/project/django-ninja-aio-crud/)
|
|
329
319
|
- **Django Ninja:** [https://django-ninja.dev/](https://django-ninja.dev/)
|
|
320
|
+
- **Example repository:** https://github.com/caspel26/ninja-aio-blog-example
|
|
330
321
|
|
|
331
322
|
---
|
|
332
323
|
|
|
@@ -6,6 +6,7 @@ import orjson
|
|
|
6
6
|
from django.http import HttpRequest
|
|
7
7
|
from django.conf import settings
|
|
8
8
|
from ninja.renderers import BaseRenderer
|
|
9
|
+
from pydantic import AnyUrl
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
class ORJSONRenderer(BaseRenderer):
|
|
@@ -38,7 +39,7 @@ class ORJSONRenderer(BaseRenderer):
|
|
|
38
39
|
def transform(cls, value):
|
|
39
40
|
if isinstance(value, bytes):
|
|
40
41
|
return base64.b64encode(value).decode()
|
|
41
|
-
if isinstance(value, (IPv4Address, IPv6Address)):
|
|
42
|
+
if isinstance(value, (IPv4Address, IPv6Address, AnyUrl)):
|
|
42
43
|
return str(value)
|
|
43
44
|
if isinstance(value, dict):
|
|
44
45
|
return {k: cls.transform(v) for k, v in value.items()}
|
|
@@ -28,9 +28,9 @@ classifiers = [
|
|
|
28
28
|
"Topic :: Internet :: WWW/HTTP",
|
|
29
29
|
]
|
|
30
30
|
|
|
31
|
-
requires = ["django-ninja >=1.3.0,
|
|
31
|
+
requires = ["django-ninja >=1.3.0, <1.6", "joserfc >=1.0.0, <= 1.4.1", "orjson >= 3.10.7, <= 3.11.5"]
|
|
32
32
|
description-file = "README.md"
|
|
33
|
-
requires-python = ">=3.10,
|
|
33
|
+
requires-python = ">=3.10, <3.15"
|
|
34
34
|
|
|
35
35
|
[tool.flit.metadata.requires-extra]
|
|
36
36
|
test = [
|
|
@@ -1,35 +0,0 @@
|
|
|
1
|
-
# Base usage example with a User model without any relation.
|
|
2
|
-
|
|
3
|
-
from django.db import models
|
|
4
|
-
from ninja_aio.models import ModelSerializer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class User(ModelSerializer):
|
|
8
|
-
username = models.CharField(max_length=150, unique=True)
|
|
9
|
-
email = models.EmailField(unique=True)
|
|
10
|
-
first_name = models.CharField(max_length=30, blank=True)
|
|
11
|
-
last_name = models.CharField(max_length=30, blank=True)
|
|
12
|
-
|
|
13
|
-
@property
|
|
14
|
-
def full_name(self):
|
|
15
|
-
return f"{self.first_name} {self.last_name}"
|
|
16
|
-
|
|
17
|
-
class ReadSerializer:
|
|
18
|
-
fields = ["id", "username", "email"]
|
|
19
|
-
customs = [
|
|
20
|
-
("full_name", str, ""),
|
|
21
|
-
]
|
|
22
|
-
|
|
23
|
-
class CreateSerializer:
|
|
24
|
-
fields = ["username", "email"]
|
|
25
|
-
optionals = [("first_name", str), ("last_name", str)]
|
|
26
|
-
|
|
27
|
-
class UpdateSerializer:
|
|
28
|
-
optionals = [
|
|
29
|
-
("email", str),
|
|
30
|
-
("first_name", str),
|
|
31
|
-
("last_name", str),
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
def __str__(self):
|
|
35
|
-
return self.username
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
# base example of a ViewSet using NinjaAIO and APIViewSet without any relation and auth.
|
|
2
|
-
|
|
3
|
-
from ninja_aio import NinjaAIO
|
|
4
|
-
from ninja_aio.views import APIViewSet
|
|
5
|
-
|
|
6
|
-
from examples.ex_1.models import User
|
|
7
|
-
|
|
8
|
-
api = NinjaAIO()
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
class UserViewSet(APIViewSet):
|
|
12
|
-
model = User
|
|
13
|
-
api = api
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
UserViewSet().add_views_to_route()
|
|
@@ -1,33 +0,0 @@
|
|
|
1
|
-
# Base usage example of a JWT authentication setup with NinjaAIO.
|
|
2
|
-
|
|
3
|
-
from ninja_aio.auth import AsyncJwtBearer
|
|
4
|
-
from ninja_aio.models import ModelUtil
|
|
5
|
-
from django.conf import settings
|
|
6
|
-
from joserfc import jwk
|
|
7
|
-
|
|
8
|
-
from examples.ex_2.models import Customer
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
PUBLIC_KEY: jwk.RSAKey = settings.JWT_PUBLIC_KEY
|
|
12
|
-
ESSENTIAL_CLAIM = {"essential": True}
|
|
13
|
-
MANDATORY_CLAIMS = {
|
|
14
|
-
"iat": ESSENTIAL_CLAIM,
|
|
15
|
-
"exp": ESSENTIAL_CLAIM,
|
|
16
|
-
"nbf": ESSENTIAL_CLAIM,
|
|
17
|
-
"aud": ESSENTIAL_CLAIM,
|
|
18
|
-
"sub": ESSENTIAL_CLAIM,
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class JwtAuth(AsyncJwtBearer):
|
|
23
|
-
jwt_public = PUBLIC_KEY
|
|
24
|
-
claims = MANDATORY_CLAIMS
|
|
25
|
-
|
|
26
|
-
async def auth_handler(self, request):
|
|
27
|
-
try:
|
|
28
|
-
request.user = await ModelUtil(Customer).get_object(
|
|
29
|
-
request, self.dcd.claims.get("sub", ""), with_qs_request=False
|
|
30
|
-
)
|
|
31
|
-
except Customer.DoesNotExist:
|
|
32
|
-
return False
|
|
33
|
-
return True
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
# Base usage example with a User model with Customer ForeignKey.
|
|
2
|
-
|
|
3
|
-
from django.db import models
|
|
4
|
-
from ninja_aio.models import ModelSerializer
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class Customer(ModelSerializer):
|
|
8
|
-
name = models.CharField(max_length=255)
|
|
9
|
-
email = models.EmailField(unique=True)
|
|
10
|
-
|
|
11
|
-
users: models.QuerySet["User"]
|
|
12
|
-
|
|
13
|
-
class ReadSerializer:
|
|
14
|
-
# Note: 'users' field will represent related User instances as list of their RelatedSerializer data
|
|
15
|
-
# This is automatically handled by NinjaAIO's ModelSerializer
|
|
16
|
-
fields = ["id", "name", "email", "users"]
|
|
17
|
-
|
|
18
|
-
class CreateSerializer:
|
|
19
|
-
fields = ["name", "email"]
|
|
20
|
-
|
|
21
|
-
class UpdateSerializer:
|
|
22
|
-
optionals = [
|
|
23
|
-
("name", str),
|
|
24
|
-
("email", str),
|
|
25
|
-
]
|
|
26
|
-
|
|
27
|
-
def __str__(self):
|
|
28
|
-
return self.name
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
class User(ModelSerializer):
|
|
32
|
-
username = models.CharField(max_length=150, unique=True)
|
|
33
|
-
email = models.EmailField(unique=True)
|
|
34
|
-
first_name = models.CharField(max_length=30, blank=True)
|
|
35
|
-
last_name = models.CharField(max_length=30, blank=True)
|
|
36
|
-
customer = models.ForeignKey(
|
|
37
|
-
Customer, on_delete=models.CASCADE, related_name="users"
|
|
38
|
-
)
|
|
39
|
-
|
|
40
|
-
@property
|
|
41
|
-
def full_name(self):
|
|
42
|
-
return f"{self.first_name} {self.last_name}"
|
|
43
|
-
|
|
44
|
-
class ReadSerializer:
|
|
45
|
-
fields = ["id", "username", "email", "customer"]
|
|
46
|
-
customs = [
|
|
47
|
-
("full_name", str, ""),
|
|
48
|
-
]
|
|
49
|
-
|
|
50
|
-
class CreateSerializer:
|
|
51
|
-
fields = ["username", "email", "customer"]
|
|
52
|
-
optionals = [("first_name", str), ("last_name", str)]
|
|
53
|
-
|
|
54
|
-
class UpdateSerializer:
|
|
55
|
-
optionals = [
|
|
56
|
-
("email", str),
|
|
57
|
-
("first_name", str),
|
|
58
|
-
("last_name", str),
|
|
59
|
-
]
|
|
60
|
-
|
|
61
|
-
def __str__(self):
|
|
62
|
-
return self.username
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
# base example of a ViewSet using NinjaAIO and APIViewSet without any relation and auth.
|
|
2
|
-
|
|
3
|
-
from ninja_aio import NinjaAIO
|
|
4
|
-
from ninja_aio.views import APIViewSet
|
|
5
|
-
|
|
6
|
-
from examples.ex_2 import models
|
|
7
|
-
from examples.ex_2.auth import JwtAuth
|
|
8
|
-
|
|
9
|
-
api = NinjaAIO()
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
class BaseAPIViewSet(APIViewSet):
|
|
13
|
-
api = api
|
|
14
|
-
auth = [JwtAuth()]
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
class CustomerViewSet(BaseAPIViewSet):
|
|
18
|
-
model = models.Customer
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
class UserViewSet(BaseAPIViewSet):
|
|
22
|
-
model = models.User
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
CustomerViewSet().add_views_to_route()
|
|
26
|
-
UserViewSet().add_views_to_route()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/models/model_serializer.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/api/renderers/orjson_renderer.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/installation.md
RENAMED
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/docs/getting_started/quick_start.md
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/decorators/__init__.py
RENAMED
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/ninja_aio/decorators/operations.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/core/test_exceptions_api.py
RENAMED
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/core/test_renderer_parser.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/helpers/test_many_to_many_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_ninja_aio_crud-2.3.0 → django_ninja_aio_crud-2.3.2}/tests/models/test_models_extra.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|