django-small-view-set 0.2.6__py3-none-any.whl → 0.2.8__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_small_view_set-0.2.6.dist-info → django_small_view_set-0.2.8.dist-info}/METADATA +41 -29
- {django_small_view_set-0.2.6.dist-info → django_small_view_set-0.2.8.dist-info}/RECORD +6 -6
- small_view_set/helpers.py +1 -1
- small_view_set/small_view_set.py +1 -155
- {django_small_view_set-0.2.6.dist-info → django_small_view_set-0.2.8.dist-info}/LICENSE +0 -0
- {django_small_view_set-0.2.6.dist-info → django_small_view_set-0.2.8.dist-info}/WHEEL +0 -0
@@ -1,10 +1,10 @@
|
|
1
1
|
Metadata-Version: 2.1
|
2
2
|
Name: django-small-view-set
|
3
|
-
Version: 0.2.
|
4
|
-
Summary: A lightweight Django ViewSet alternative with minimal abstraction
|
3
|
+
Version: 0.2.8
|
4
|
+
Summary: A lightweight and explicit Django ViewSet alternative with minimal abstraction and full async support
|
5
5
|
Home-page: https://github.com/nateonguitar/django-small-view-set
|
6
6
|
License: MIT
|
7
|
-
Keywords: django,
|
7
|
+
Keywords: django,viewset,view set,api,async,rest api,django async,lightweight
|
8
8
|
Author: Nate Brooks
|
9
9
|
Requires-Python: >=3.8
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -19,16 +19,12 @@ Description-Content-Type: text/markdown
|
|
19
19
|
|
20
20
|
# Django Small View Set
|
21
21
|
|
22
|
-
A lightweight Django ViewSet alternative with minimal abstraction
|
22
|
+
A lightweight and explicit Django ViewSet alternative with minimal abstraction and full async support.
|
23
23
|
|
24
|
-
|
25
|
-
|
26
|
-
This guide provides a simple example to get started with the library.
|
24
|
+
Designed for clear patterns, minimal magic, and complete control over your API endpoints.
|
27
25
|
|
28
26
|
### Example Usage
|
29
27
|
|
30
|
-
Here’s how to define a basic view set:
|
31
|
-
|
32
28
|
In settings.py
|
33
29
|
```python
|
34
30
|
# Register SmallViewSetConfig in settings
|
@@ -37,43 +33,60 @@ from small_view_set SmallViewSetConfig
|
|
37
33
|
SMALL_VIEW_SET_CONFIG = SmallViewSetConfig()
|
38
34
|
```
|
39
35
|
|
36
|
+
^^^ This will get you up and running, but it is recommended to write your own [Custom exception handler](./README_CUSTOM_EXCEPTION_HANDLER.md)
|
37
|
+
|
38
|
+
Please note, endpoints cannot be registered in `urls.py` with the
|
39
|
+
request method (like POST, or GET), therefore create a `collection` and/or `detail` orchestrator
|
40
|
+
method for the standard CRUD operations.
|
41
|
+
|
40
42
|
|
41
43
|
```python
|
42
44
|
import asyncio
|
43
45
|
from django.http import JsonResponse
|
44
46
|
from django.urls import path
|
45
47
|
from small_view_set import SmallViewSet, endpoint, endpoint_disabled
|
48
|
+
from urllib.request import Request
|
46
49
|
|
47
50
|
class BarViewSet(SmallViewSet):
|
48
51
|
|
49
52
|
def urlpatterns(self):
|
50
53
|
return [
|
51
|
-
path('api/bars/', self.
|
52
|
-
path('api/bars
|
53
|
-
path('api/bars
|
54
|
+
path('api/bars/', self.collection, name='bars_collection'),
|
55
|
+
path('api/bars/<int:pk>/', self.detail, name='bars_detail'),
|
56
|
+
path('api/bars/items/', self.items, name='bars_items'),
|
54
57
|
]
|
58
|
+
@endpoint(allowed_methods=['GET', 'POST'])
|
59
|
+
def collection(self, request: Request):
|
60
|
+
if request.method == 'GET':
|
61
|
+
return self.list(request)
|
62
|
+
raise MethodNotAllowed(request.method)
|
63
|
+
|
64
|
+
@endpoint(allowed_methods=['GET', 'PATCH'])
|
65
|
+
async def detail(self, request: Request, pk: int):
|
66
|
+
if request.method == 'GET':
|
67
|
+
return await self.retrieve(request, pk)
|
68
|
+
elif request.method == 'PATCH':
|
69
|
+
return self.patch(request, pk)
|
70
|
+
raise MethodNotAllowed(request.method)
|
55
71
|
|
56
|
-
@endpoint(allowed_methods=['GET'])
|
57
72
|
def list(self, request):
|
58
73
|
self.protect_list(request)
|
59
74
|
return JsonResponse({"message": "Hello, world!"}, status=200)
|
60
75
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
self.protect_list(request)
|
65
|
-
await asyncio.sleep(1)
|
66
|
-
return JsonResponse({"message": "List of items"}, status=200)
|
76
|
+
async def retrieve(self, request: Request, pk: int):
|
77
|
+
self.protect_retrieve(request)
|
78
|
+
return JsonResponse({"message": f"Detail for ID {pk}"}, status=200)
|
67
79
|
|
68
|
-
|
69
|
-
def patch(self, request, pk):
|
80
|
+
def patch(self, request: Request, pk: int):
|
70
81
|
self.protect_update(request)
|
71
82
|
return JsonResponse({"message": f"Updated {pk}"}, status=200)
|
72
83
|
|
73
84
|
@endpoint(allowed_methods=['GET'])
|
74
|
-
async def
|
75
|
-
|
76
|
-
|
85
|
+
async def items(self, request: Request):
|
86
|
+
# Pick the closest protect that matches the endoint. `GET items` is closest to a list
|
87
|
+
self.protect_list(request)
|
88
|
+
await asyncio.sleep(1)
|
89
|
+
return JsonResponse({"message": "List of items"}, status=200)
|
77
90
|
```
|
78
91
|
|
79
92
|
|
@@ -92,12 +105,11 @@ urlpatterns = [
|
|
92
105
|
```
|
93
106
|
|
94
107
|
|
95
|
-
##
|
108
|
+
## Deeper learning
|
96
109
|
|
97
|
-
- [Custom
|
98
|
-
- [
|
99
|
-
- [
|
100
|
-
- [DRF Compatibility](./README_DRF_COMPATIBILITY.md): Learn how to use some of Django Rest Framework's tools, like Serializers.
|
110
|
+
- [Custom protections](./README_CUSTOM_PROTECTIONS.md): Learn how to subclass `SmallViewSet` to add custom protections like logged-in checks.
|
111
|
+
- [Custom exception handler](./README_CUSTOM_EXCEPTION_HANDLER.md): Understand how to write your own exception handler.
|
112
|
+
- [DRF compatibility](./README_DRF_COMPATIBILITY.md): Learn how to use some of Django Rest Framework's tools, like Serializers.
|
101
113
|
- [Disabling an endpoint](./README_DISABLE_ENDPOINT.md): Learn how to disable an endpoint without needing to delete it or comment it out.
|
102
114
|
- [Reason](./README_REASON.md): Reasoning behind this package.
|
103
115
|
|
@@ -3,9 +3,9 @@ small_view_set/__init__.py,sha256=lJ09qERCFrf4WAY6mRFyFsEG1R3ErpWqblIKi4TVggI,62
|
|
3
3
|
small_view_set/config.py,sha256=nGu840qa6zYyXoQa1EO7xIlWh3dFPd0oYwN-2Qmj7Ws,1446
|
4
4
|
small_view_set/decorators.py,sha256=o5qkbnhFKVL47PVZKozyQoC3beuzl6MtsleKhKHofj8,3322
|
5
5
|
small_view_set/exceptions.py,sha256=5VaPS9m9syhag7p-gveG7Dep4DV3YQ7WjI1Sv5C_1N0,966
|
6
|
-
small_view_set/helpers.py,sha256=
|
7
|
-
small_view_set/small_view_set.py,sha256
|
8
|
-
django_small_view_set-0.2.
|
9
|
-
django_small_view_set-0.2.
|
10
|
-
django_small_view_set-0.2.
|
11
|
-
django_small_view_set-0.2.
|
6
|
+
small_view_set/helpers.py,sha256=C05Q-KVeAvmOg7hhNVO3-ti8k0esidZ5OpRQUQNEH40,5126
|
7
|
+
small_view_set/small_view_set.py,sha256=-MYj1qbGRD0mLuWoJFxua7fwAry7crgMSZHCzqL2PVc,2581
|
8
|
+
django_small_view_set-0.2.8.dist-info/LICENSE,sha256=M4ZuHeiGHHuewaZyqz7tol4E6E2GMz7fF1ywNoXD1tA,1069
|
9
|
+
django_small_view_set-0.2.8.dist-info/METADATA,sha256=kJEHV5P_ThuRwMF6Drn4F7ZAQtFOrqaR0AVNvYT_a9g,4241
|
10
|
+
django_small_view_set-0.2.8.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
11
|
+
django_small_view_set-0.2.8.dist-info/RECORD,,
|
small_view_set/helpers.py
CHANGED
@@ -18,7 +18,7 @@ if not _logger.hasHandlers():
|
|
18
18
|
_logger.setLevel(logging.INFO)
|
19
19
|
|
20
20
|
|
21
|
-
def default_options_and_head_handler(request, allowed_methods: list[str]):
|
21
|
+
def default_options_and_head_handler(request: Request, allowed_methods: list[str]):
|
22
22
|
if request.method == 'OPTIONS':
|
23
23
|
response = JsonResponse(
|
24
24
|
data=None,
|
small_view_set/small_view_set.py
CHANGED
@@ -1,11 +1,8 @@
|
|
1
|
-
import inspect
|
2
1
|
import json
|
3
2
|
import logging
|
4
3
|
from urllib.request import Request
|
5
4
|
|
6
|
-
|
7
|
-
from .decorators import endpoint
|
8
|
-
from .exceptions import BadRequest, MethodNotAllowed
|
5
|
+
from .exceptions import BadRequest
|
9
6
|
|
10
7
|
logger = logging.getLogger('app')
|
11
8
|
|
@@ -79,154 +76,3 @@ class SmallViewSet:
|
|
79
76
|
this library adds logic in the future.
|
80
77
|
"""
|
81
78
|
pass
|
82
|
-
|
83
|
-
@endpoint(allowed_methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'])
|
84
|
-
async def default_router_async(self, request: Request, pk=None, *args, **kwargs):
|
85
|
-
"""
|
86
|
-
This method routes requests to the appropriate method based on the HTTP method and presence of a primary key (pk).
|
87
|
-
|
88
|
-
It also handles errors and returns appropriate JSON responses by using the decorator @endpoint(allowed_method=[]).
|
89
|
-
|
90
|
-
GET/POST for collection endpoints and GET/PUT/PATCH/DELETE for detail endpoints.
|
91
|
-
|
92
|
-
Example:
|
93
|
-
```
|
94
|
-
# Note: AppViewSet is a subclass of SmallViewSet with overridden protect methods with more specific logic.
|
95
|
-
|
96
|
-
class CommentViewSet(AppViewSet):
|
97
|
-
def urlpatterns(self):
|
98
|
-
return [
|
99
|
-
path('api/comments/', self.default_router_async, name='comments_collection'),
|
100
|
-
path('api/comments/<int:pk>/', self.default_router_async, name='comments_detail'),
|
101
|
-
path('api/comments/<int:pk>/custom_put/', self.custom_put, name='comments_custom_put_detail'),
|
102
|
-
]
|
103
|
-
|
104
|
-
@endpoint(allowed_method=['POST'])
|
105
|
-
def create(self, request: Request):
|
106
|
-
self.protect_create(request)
|
107
|
-
. . .
|
108
|
-
|
109
|
-
@endpoint(allowed_method=['PUT', 'PATCH'])
|
110
|
-
def update(self, request: Request, pk: int):
|
111
|
-
self.protect_update(request)
|
112
|
-
. . .
|
113
|
-
|
114
|
-
@endpoint(allowed_method=['PUT'])
|
115
|
-
def custom_put(self, request: Request, pk: int):
|
116
|
-
self.protect_update(request)
|
117
|
-
. . .
|
118
|
-
|
119
|
-
@endpoint(allowed_method=['GET'])
|
120
|
-
@disable_endpoint
|
121
|
-
def some_disabled_endpoint(self, request: Request):
|
122
|
-
self.protect_retrieve(request)
|
123
|
-
. . .
|
124
|
-
```
|
125
|
-
"""
|
126
|
-
func = None
|
127
|
-
if pk is None:
|
128
|
-
if request.method == 'GET':
|
129
|
-
if hasattr(self, 'list'):
|
130
|
-
func = self.list
|
131
|
-
|
132
|
-
elif request.method == 'POST':
|
133
|
-
if hasattr(self, 'create'):
|
134
|
-
func = self.create
|
135
|
-
else:
|
136
|
-
if request.method == 'GET':
|
137
|
-
if hasattr(self, 'retrieve'):
|
138
|
-
func = self.retrieve
|
139
|
-
|
140
|
-
elif request.method == 'PUT':
|
141
|
-
if hasattr(self, 'put'):
|
142
|
-
func = self.put
|
143
|
-
|
144
|
-
elif request.method == 'PATCH':
|
145
|
-
if hasattr(self, 'patch'):
|
146
|
-
func = self.patch
|
147
|
-
|
148
|
-
elif request.method == 'DELETE':
|
149
|
-
if hasattr(self, 'delete'):
|
150
|
-
func = self.delete
|
151
|
-
|
152
|
-
if func is None:
|
153
|
-
raise MethodNotAllowed(request.method)
|
154
|
-
if pk is not None:
|
155
|
-
kwargs['pk'] = pk
|
156
|
-
return await func(request, *args, **kwargs)
|
157
|
-
|
158
|
-
|
159
|
-
@endpoint(allowed_methods=['GET', 'POST', 'PUT', 'DELETE', 'OPTIONS', 'HEAD'])
|
160
|
-
def default_router(self, request: Request, pk=None, *args, **kwargs):
|
161
|
-
"""
|
162
|
-
This method routes requests to the appropriate method based on the HTTP method and presence of a primary key (pk).
|
163
|
-
|
164
|
-
It also handles errors and returns appropriate JSON responses by using the decorator @endpoint(allowed_method=[]).
|
165
|
-
|
166
|
-
GET/POST for collection endpoints and GET/PUT/PATCH/DELETE for detail endpoints.
|
167
|
-
|
168
|
-
Example:
|
169
|
-
```
|
170
|
-
# Note: AppViewSet is a subclass of SmallViewSet with overridden protect methods with more specific logic.
|
171
|
-
|
172
|
-
class CommentViewSet(AppViewSet):
|
173
|
-
def urlpatterns(self):
|
174
|
-
return [
|
175
|
-
path('api/comments/', self.default_router, name='comments_collection'),
|
176
|
-
path('api/comments/<int:pk>/', self.default_router, name='comments_detail'),
|
177
|
-
path('api/comments/<int:pk>/custom_put/', self.custom_put, name='comments_custom_put_detail'),
|
178
|
-
]
|
179
|
-
|
180
|
-
@endpoint(allowed_method=['POST'])
|
181
|
-
def create(self, request: Request):
|
182
|
-
self.protect_create(request)
|
183
|
-
. . .
|
184
|
-
|
185
|
-
@endpoint(allowed_method=['PUT', 'PATCH'])
|
186
|
-
def update(self, request: Request, pk: int):
|
187
|
-
self.protect_update(request)
|
188
|
-
. . .
|
189
|
-
|
190
|
-
@endpoint(allowed_method=['PUT'])
|
191
|
-
def custom_put(self, request: Request, pk: int):
|
192
|
-
self.protect_update(request)
|
193
|
-
. . .
|
194
|
-
|
195
|
-
@disable_endpoint
|
196
|
-
@endpoint(allowed_method=['GET'])
|
197
|
-
def some_disabled_endpoint(self, request: Request):
|
198
|
-
self.protect_retrieve(request)
|
199
|
-
. . .
|
200
|
-
```
|
201
|
-
"""
|
202
|
-
func = None
|
203
|
-
if pk is None:
|
204
|
-
if request.method == 'GET':
|
205
|
-
if hasattr(self, 'list'):
|
206
|
-
func = self.list
|
207
|
-
|
208
|
-
elif request.method == 'POST':
|
209
|
-
if hasattr(self, 'create'):
|
210
|
-
func = self.create
|
211
|
-
else:
|
212
|
-
if request.method == 'GET':
|
213
|
-
if hasattr(self, 'retrieve'):
|
214
|
-
func = self.retrieve
|
215
|
-
|
216
|
-
elif request.method == 'PUT':
|
217
|
-
if hasattr(self, 'put'):
|
218
|
-
func = self.put
|
219
|
-
|
220
|
-
elif request.method == 'PATCH':
|
221
|
-
if hasattr(self, 'patch'):
|
222
|
-
func = self.patch
|
223
|
-
|
224
|
-
elif request.method == 'DELETE':
|
225
|
-
if hasattr(self, 'delete'):
|
226
|
-
func = self.delete
|
227
|
-
|
228
|
-
if func is None:
|
229
|
-
raise MethodNotAllowed(request.method)
|
230
|
-
if pk is not None:
|
231
|
-
kwargs['pk'] = pk
|
232
|
-
return func(request, *args, **kwargs)
|
File without changes
|
File without changes
|