django-small-view-set 0.1.4__py3-none-any.whl → 0.2.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.
- {django_small_view_set-0.1.4.dist-info → django_small_view_set-0.2.1.dist-info}/METADATA +20 -10
- django_small_view_set-0.2.1.dist-info/RECORD +11 -0
- small_view_set/__init__.py +15 -4
- small_view_set/config.py +30 -0
- small_view_set/decorators.py +45 -169
- small_view_set/helpers.py +133 -0
- small_view_set/small_view_set.py +46 -25
- django_small_view_set-0.1.4.dist-info/RECORD +0 -9
- {django_small_view_set-0.1.4.dist-info → django_small_view_set-0.2.1.dist-info}/LICENSE +0 -0
- {django_small_view_set-0.1.4.dist-info → django_small_view_set-0.2.1.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.1
|
3
|
+
Version: 0.2.1
|
4
4
|
Summary: A lightweight Django ViewSet alternative with minimal abstraction.
|
5
5
|
Home-page: https://github.com/nateonguitar/django-small-view-set
|
6
6
|
License: MIT
|
7
|
-
Keywords: django,viewset
|
7
|
+
Keywords: django,small,viewset,view set
|
8
8
|
Author: Nate Brooks
|
9
9
|
Requires-Python: >=3.8
|
10
10
|
Classifier: License :: OSI Approved :: MIT License
|
@@ -14,6 +14,7 @@ Classifier: Programming Language :: Python :: 3.9
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.10
|
15
15
|
Classifier: Programming Language :: Python :: 3.11
|
16
16
|
Requires-Dist: django (>=3.2)
|
17
|
+
Requires-Dist: pytest-django (>=4.11.1,<5.0.0)
|
17
18
|
Project-URL: Repository, https://github.com/nateonguitar/django-small-view-set
|
18
19
|
Description-Content-Type: text/markdown
|
19
20
|
|
@@ -27,41 +28,50 @@ This guide provides a simple example to get started with the library.
|
|
27
28
|
|
28
29
|
### Example Usage
|
29
30
|
|
30
|
-
Here’s how
|
31
|
+
Here’s how to define a basic view set:
|
32
|
+
|
33
|
+
In settings.py
|
34
|
+
```python
|
35
|
+
# Register SmallViewSetConfig in settings
|
36
|
+
from small_view_set SmallViewSetConfig
|
37
|
+
|
38
|
+
SMALL_VIEW_SET_CONFIG = SmallViewSetConfig()
|
39
|
+
```
|
40
|
+
|
31
41
|
|
32
42
|
```python
|
33
43
|
import asyncio
|
34
44
|
from django.http import JsonResponse
|
35
45
|
from django.urls import path
|
36
|
-
from small_view_set
|
46
|
+
from small_view_set import SmallViewSet, endpoint, endpoint_disabled
|
37
47
|
|
38
48
|
class BarViewSet(SmallViewSet):
|
39
49
|
|
40
50
|
def urlpatterns(self):
|
41
51
|
return [
|
42
52
|
path('api/bars/', self.default_router, name='bars_collection'),
|
43
|
-
path('api/bars/items/', self.items,
|
53
|
+
path('api/bars/items/', self.items, name='bars_items'),
|
44
54
|
path('api/bars/<int:pk>/', self.default_router, name='bars_detail'),
|
45
55
|
]
|
46
56
|
|
47
|
-
@
|
57
|
+
@endpoint(allowed_methods=['GET'])
|
48
58
|
def list(self, request):
|
49
59
|
self.protect_list(request)
|
50
60
|
return JsonResponse({"message": "Hello, world!"}, status=200)
|
51
61
|
|
52
|
-
@
|
53
|
-
@
|
62
|
+
@endpoint(allowed_methods=['GET'])
|
63
|
+
@endpoint_disabled
|
54
64
|
async def items(self, request):
|
55
65
|
self.protect_list(request)
|
56
66
|
await asyncio.sleep(1)
|
57
67
|
return JsonResponse({"message": "List of items"}, status=200)
|
58
68
|
|
59
|
-
@
|
69
|
+
@endpoint(allowed_methods=['PATCH'])
|
60
70
|
def patch(self, request, pk):
|
61
71
|
self.protect_update(request)
|
62
72
|
return JsonResponse({"message": f"Updated {pk}"}, status=200)
|
63
73
|
|
64
|
-
@
|
74
|
+
@endpoint(allowed_methods=['GET'])
|
65
75
|
async def retrieve(self, request, pk):
|
66
76
|
self.protect_retrieve(request)
|
67
77
|
return JsonResponse({"message": f"Detail for ID {pk}"}, status=200)
|
@@ -0,0 +1,11 @@
|
|
1
|
+
small_view_set/README.md,sha256=oovLoOhBsg5wK8AwGe9iyuWXStHUo9x4fsrlb-oxHt4,1061
|
2
|
+
small_view_set/__init__.py,sha256=lJ09qERCFrf4WAY6mRFyFsEG1R3ErpWqblIKi4TVggI,625
|
3
|
+
small_view_set/config.py,sha256=nGu840qa6zYyXoQa1EO7xIlWh3dFPd0oYwN-2Qmj7Ws,1446
|
4
|
+
small_view_set/decorators.py,sha256=0XSnTdmhvxnkLaRanoZhZq3BNiuP8JCe4vfVFWrRQkw,3200
|
5
|
+
small_view_set/exceptions.py,sha256=5VaPS9m9syhag7p-gveG7Dep4DV3YQ7WjI1Sv5C_1N0,966
|
6
|
+
small_view_set/helpers.py,sha256=EKx71YLS-5cN-JrMJXYR39O15qf4O6TDiaQsal7BVwY,5117
|
7
|
+
small_view_set/small_view_set.py,sha256=wvNY1s3DRe2pLLLxz_91MCruEDl3iScOtDa8Y8L4siQ,5566
|
8
|
+
django_small_view_set-0.2.1.dist-info/LICENSE,sha256=M4ZuHeiGHHuewaZyqz7tol4E6E2GMz7fF1ywNoXD1tA,1069
|
9
|
+
django_small_view_set-0.2.1.dist-info/METADATA,sha256=UHgGDqDGiv6zTLzedn_jUtnCmCQigAgDViAhZ-hRIzQ,3607
|
10
|
+
django_small_view_set-0.2.1.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
11
|
+
django_small_view_set-0.2.1.dist-info/RECORD,,
|
small_view_set/__init__.py
CHANGED
@@ -1,7 +1,12 @@
|
|
1
1
|
from .small_view_set import SmallViewSet
|
2
|
+
from .config import SmallViewSetConfig
|
2
3
|
from .decorators import (
|
3
|
-
|
4
|
-
|
4
|
+
endpoint,
|
5
|
+
endpoint_disabled,
|
6
|
+
)
|
7
|
+
from.helpers import (
|
8
|
+
default_exception_handler,
|
9
|
+
default_options_and_head_handler,
|
5
10
|
)
|
6
11
|
from .exceptions import (
|
7
12
|
BadRequest,
|
@@ -12,8 +17,14 @@ from .exceptions import (
|
|
12
17
|
|
13
18
|
__all__ = [
|
14
19
|
"SmallViewSet",
|
15
|
-
"
|
16
|
-
|
20
|
+
"SmallViewSetConfig",
|
21
|
+
|
22
|
+
"endpoint",
|
23
|
+
"endpoint_disabled",
|
24
|
+
|
25
|
+
"default_exception_handler",
|
26
|
+
"default_options_and_head_handler",
|
27
|
+
|
17
28
|
"BadRequest",
|
18
29
|
"EndpointDisabledException",
|
19
30
|
"MethodNotAllowed",
|
small_view_set/config.py
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
from typing import Callable
|
2
|
+
from urllib.request import Request
|
3
|
+
from .helpers import default_exception_handler, default_options_and_head_handler
|
4
|
+
|
5
|
+
|
6
|
+
class SmallViewSetConfig:
|
7
|
+
"""
|
8
|
+
Configuration class for SmallViewSet.
|
9
|
+
|
10
|
+
This class allows customization of exception handling and handling of
|
11
|
+
OPTIONS and HEAD requests for endpoints.
|
12
|
+
|
13
|
+
Args:
|
14
|
+
exception_handler (Callable[[str, Exception], None]): A callback function
|
15
|
+
for handling exceptions. The function takes two parameters:
|
16
|
+
1. The name of the endpoint function (e.g., 'list', 'retrieve', or a custom endpoint name).
|
17
|
+
2. The exception that was thrown.
|
18
|
+
options_and_head_handler (Callable[[Request, list[str]], None]): A callback function
|
19
|
+
for handling OPTIONS and HEAD requests. The function takes two parameters:
|
20
|
+
1. The Django Request object.
|
21
|
+
2. A list of allowed HTTP methods for the endpoint (e.g., ['PUT', 'PATCH']).
|
22
|
+
"""
|
23
|
+
def __init__(
|
24
|
+
self,
|
25
|
+
exception_handler: Callable[[str, Exception], None] = default_exception_handler,
|
26
|
+
options_and_head_handler: Callable[[Request, list[str]], None] = default_options_and_head_handler,
|
27
|
+
respect_disabled_endpoints=True):
|
28
|
+
self.exception_handler = exception_handler
|
29
|
+
self.options_and_head_handler = options_and_head_handler
|
30
|
+
self.respect_disabled_endpoints = respect_disabled_endpoints
|
small_view_set/decorators.py
CHANGED
@@ -1,176 +1,52 @@
|
|
1
1
|
import inspect
|
2
|
-
import logging
|
3
2
|
from django.conf import settings
|
4
3
|
|
5
|
-
import
|
6
|
-
from
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
under the `LOGGING` dictionary. For example:
|
23
|
-
|
24
|
-
LOGGING = {
|
25
|
-
'version': 1,
|
26
|
-
'disable_existing_loggers': False,
|
27
|
-
'handlers': {
|
28
|
-
'console': {
|
29
|
-
'class': 'logging.StreamHandler',
|
30
|
-
},
|
31
|
-
},
|
32
|
-
'loggers': {
|
33
|
-
'django-small-view-set.default_handle_endpoint_exceptions': {
|
34
|
-
'handlers': ['console'],
|
35
|
-
'level': 'INFO',
|
36
|
-
'propagate': True,
|
37
|
-
},
|
38
|
-
},
|
39
|
-
}
|
40
|
-
|
41
|
-
This configuration ensures that the logger named
|
42
|
-
'django-small-view-set.default_handle_endpoint_exceptions' uses the specified
|
43
|
-
handlers and logging level.
|
44
|
-
"""
|
45
|
-
logger = logging.getLogger(name)
|
46
|
-
|
47
|
-
# Check if Django's logging is configured
|
48
|
-
if not logger.hasHandlers():
|
49
|
-
# Fallback logger setup
|
50
|
-
handler = logging.StreamHandler()
|
51
|
-
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
52
|
-
handler.setFormatter(formatter)
|
53
|
-
logger.addHandler(handler)
|
54
|
-
logger.setLevel(logging.INFO)
|
55
|
-
|
56
|
-
return logger
|
57
|
-
|
58
|
-
|
59
|
-
def default_handle_endpoint_exceptions(func):
|
60
|
-
logger = _get_logger('django-small-view-set.default_handle_endpoint_exceptions')
|
61
|
-
|
62
|
-
def _exception_handler(e):
|
63
|
-
try:
|
64
|
-
raise e
|
65
|
-
|
66
|
-
except json.JSONDecodeError:
|
67
|
-
return JsonResponse(data={"errors": "Invalid JSON"}, status=400)
|
68
|
-
|
69
|
-
except (TypeError, ValueError) as e:
|
70
|
-
if hasattr(e, 'detail'):
|
71
|
-
return JsonResponse(data={'errors': e.detail}, status=400)
|
72
|
-
if hasattr(e, 'message'):
|
73
|
-
return JsonResponse(data={'errors': e.message}, status=400)
|
74
|
-
return JsonResponse(data=None, safe=False, status=400)
|
75
|
-
|
76
|
-
except Unauthorized:
|
77
|
-
return JsonResponse(data=None, safe=False, status=401)
|
78
|
-
|
79
|
-
except (PermissionDenied, SuspiciousOperation):
|
80
|
-
return JsonResponse(data=None, safe=False, status=403)
|
81
|
-
|
82
|
-
except (Http404, ObjectDoesNotExist):
|
83
|
-
return JsonResponse(data=None, safe=False, status=404)
|
84
|
-
|
85
|
-
except EndpointDisabledException:
|
86
|
-
return JsonResponse(data=None, safe=False, status=405)
|
87
|
-
|
88
|
-
except MethodNotAllowed as e:
|
89
|
-
return JsonResponse(
|
90
|
-
data={'errors': f"Method {e.method} is not allowed"},
|
91
|
-
status=405)
|
92
|
-
|
93
|
-
except Exception as e:
|
94
|
-
# Catch-all exception handler for API endpoints.
|
95
|
-
#
|
96
|
-
# - Always defaults to HTTP 500 with "Internal server error" unless the exception provides a more specific status code and error details.
|
97
|
-
# - Duck types to extract error information from `detail` or `message` attributes, if available.
|
98
|
-
# - Never exposes internal exception contents to end users for 5xx server errors unless settings.DEBUG is True.
|
99
|
-
# - Allows structured error payloads (string, list, or dict) without assumptions about the error format.
|
100
|
-
# - Logs exceptions fully for server-side diagnostics, distinguishing handled vs unhandled cases.
|
101
|
-
#
|
102
|
-
# This design prioritizes API security, developer debugging, and future portability across projects.
|
103
|
-
|
104
|
-
status_code = getattr(e, 'status_code', 500)
|
105
|
-
error_contents = None
|
106
|
-
|
107
|
-
if hasattr(e, 'detail'):
|
108
|
-
error_contents = e.detail
|
109
|
-
elif hasattr(e, 'message') and isinstance(e.message, str):
|
110
|
-
error_contents = e.message
|
111
|
-
|
112
|
-
if 400 <= status_code <= 499:
|
113
|
-
if status_code == 400:
|
114
|
-
message = 'Bad request'
|
115
|
-
elif status_code == 401:
|
116
|
-
message = 'Unauthorized'
|
117
|
-
elif status_code == 403:
|
118
|
-
message = 'Forbidden'
|
119
|
-
elif status_code == 404:
|
120
|
-
message = 'Not found'
|
121
|
-
elif status_code == 405:
|
122
|
-
message = 'Method not allowed'
|
123
|
-
elif status_code == 429:
|
124
|
-
message = 'Too many requests'
|
125
|
-
elif error_contents:
|
126
|
-
message = error_contents
|
4
|
+
from .config import SmallViewSetConfig
|
5
|
+
from .exceptions import EndpointDisabledException
|
6
|
+
|
7
|
+
def endpoint(
|
8
|
+
allowed_methods: list[str]):
|
9
|
+
def decorator(func):
|
10
|
+
func_name = func.__name__
|
11
|
+
def sync_wrapper(viewset, *args, **kwargs):
|
12
|
+
request = args[0]
|
13
|
+
try:
|
14
|
+
config: SmallViewSetConfig = getattr(settings, 'SMALL_VIEW_SET_CONFIG', SmallViewSetConfig())
|
15
|
+
pre_response = config.options_and_head_handler(request, allowed_methods)
|
16
|
+
if pre_response:
|
17
|
+
return pre_response
|
18
|
+
pk = kwargs.pop('pk', None)
|
19
|
+
if pk is None:
|
20
|
+
return func(viewset, request=request)
|
127
21
|
else:
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
logger.error(msg)
|
147
|
-
|
148
|
-
|
149
|
-
return JsonResponse(
|
150
|
-
data={'errors': message},
|
151
|
-
safe=False,
|
152
|
-
status=status_code,
|
153
|
-
content_type='application/json')
|
154
|
-
|
155
|
-
def sync_wrapper(*args, **kwargs):
|
156
|
-
try:
|
157
|
-
return func(*args, **kwargs)
|
158
|
-
except Exception as e:
|
159
|
-
return _exception_handler(e)
|
22
|
+
return func(viewset, request=request, pk=pk)
|
23
|
+
except Exception as e:
|
24
|
+
return config.exception_handler(request, func_name, e)
|
25
|
+
|
26
|
+
async def async_wrapper(viewset, *args, **kwargs):
|
27
|
+
request = args[0]
|
28
|
+
try:
|
29
|
+
config: SmallViewSetConfig = getattr(settings, 'SMALL_VIEW_SET_CONFIG', SmallViewSetConfig())
|
30
|
+
pre_response = config.options_and_head_handler(request, allowed_methods)
|
31
|
+
if pre_response:
|
32
|
+
return pre_response
|
33
|
+
pk = kwargs.pop('pk', None)
|
34
|
+
if pk is None:
|
35
|
+
return await func(viewset, request=request)
|
36
|
+
else:
|
37
|
+
return await func(viewset, request=request, pk=pk)
|
38
|
+
except Exception as e:
|
39
|
+
return config.exception_handler(request, func_name, e)
|
160
40
|
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
return _exception_handler(e)
|
41
|
+
if inspect.iscoroutinefunction(func):
|
42
|
+
return async_wrapper
|
43
|
+
else:
|
44
|
+
return sync_wrapper
|
166
45
|
|
167
|
-
|
168
|
-
return async_wrapper
|
169
|
-
else:
|
170
|
-
return sync_wrapper
|
46
|
+
return decorator
|
171
47
|
|
172
48
|
|
173
|
-
def
|
49
|
+
def endpoint_disabled(func):
|
174
50
|
"""
|
175
51
|
Temporarily disables an API endpoint based on the SMALL_VIEWSET_RESPECT_DISABLED_ENDPOINTS setting.
|
176
52
|
|
@@ -186,20 +62,20 @@ def disable_endpoint(func):
|
|
186
62
|
class ExampleViewSet(SmallViewSet):
|
187
63
|
|
188
64
|
@default_handle_endpoint_exceptions
|
189
|
-
@
|
65
|
+
@endpoint_disabled
|
190
66
|
def retrieve(self, request: Request) -> JsonResponse:
|
191
67
|
self.protect_retrieve(request)
|
192
68
|
. . .
|
193
69
|
```
|
194
70
|
"""
|
195
|
-
|
71
|
+
config: SmallViewSetConfig = getattr(settings, 'SMALL_VIEW_SET_CONFIG', SmallViewSetConfig())
|
196
72
|
def sync_wrapper(*args, **kwargs):
|
197
|
-
if respect_disabled_endpoints:
|
73
|
+
if config.respect_disabled_endpoints:
|
198
74
|
raise EndpointDisabledException()
|
199
75
|
return func(*args, **kwargs)
|
200
76
|
|
201
77
|
async def async_wrapper(*args, **kwargs):
|
202
|
-
if respect_disabled_endpoints:
|
78
|
+
if config.respect_disabled_endpoints:
|
203
79
|
raise EndpointDisabledException()
|
204
80
|
return await func(*args, **kwargs)
|
205
81
|
|
@@ -0,0 +1,133 @@
|
|
1
|
+
import json
|
2
|
+
import logging
|
3
|
+
|
4
|
+
from django.conf import settings
|
5
|
+
from django.core.exceptions import ObjectDoesNotExist, SuspiciousOperation, PermissionDenied
|
6
|
+
from django.http import Http404, JsonResponse
|
7
|
+
from urllib.request import Request
|
8
|
+
|
9
|
+
from .exceptions import EndpointDisabledException, MethodNotAllowed, Unauthorized
|
10
|
+
|
11
|
+
|
12
|
+
_logger = logging.getLogger('django-small-view-set.default_handle_endpoint_exceptions')
|
13
|
+
if not _logger.hasHandlers():
|
14
|
+
handler = logging.StreamHandler()
|
15
|
+
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
|
16
|
+
handler.setFormatter(formatter)
|
17
|
+
_logger.addHandler(handler)
|
18
|
+
_logger.setLevel(logging.INFO)
|
19
|
+
|
20
|
+
|
21
|
+
def default_options_and_head_handler(request, allowed_methods: list[str]):
|
22
|
+
if request.method == 'OPTIONS':
|
23
|
+
response = JsonResponse(
|
24
|
+
data=None,
|
25
|
+
safe=False,
|
26
|
+
status=200,
|
27
|
+
content_type='application/json')
|
28
|
+
response['Allow'] = ', '.join(allowed_methods)
|
29
|
+
return response
|
30
|
+
|
31
|
+
if request.method == 'HEAD':
|
32
|
+
response = JsonResponse(
|
33
|
+
data=None,
|
34
|
+
safe=False,
|
35
|
+
status=200,
|
36
|
+
content_type='application/json')
|
37
|
+
response['Allow'] = ', '.join(allowed_methods)
|
38
|
+
return response
|
39
|
+
|
40
|
+
if request.method not in allowed_methods:
|
41
|
+
raise MethodNotAllowed(method=request.method)
|
42
|
+
|
43
|
+
|
44
|
+
def default_exception_handler(request: Request, endpoint_name: str, exception):
|
45
|
+
try:
|
46
|
+
raise exception
|
47
|
+
|
48
|
+
except json.JSONDecodeError:
|
49
|
+
return JsonResponse(data={"errors": "Invalid JSON"}, status=400)
|
50
|
+
|
51
|
+
except (TypeError, ValueError) as exception:
|
52
|
+
if hasattr(exception, 'detail'):
|
53
|
+
return JsonResponse(data={'errors': exception.detail}, status=400)
|
54
|
+
if hasattr(exception, 'message'):
|
55
|
+
return JsonResponse(data={'errors': exception.message}, status=400)
|
56
|
+
return JsonResponse(data=None, safe=False, status=400)
|
57
|
+
|
58
|
+
except Unauthorized:
|
59
|
+
return JsonResponse(data=None, safe=False, status=401)
|
60
|
+
|
61
|
+
except (PermissionDenied, SuspiciousOperation):
|
62
|
+
return JsonResponse(data=None, safe=False, status=403)
|
63
|
+
|
64
|
+
except (Http404, ObjectDoesNotExist):
|
65
|
+
return JsonResponse(data=None, safe=False, status=404)
|
66
|
+
|
67
|
+
except EndpointDisabledException:
|
68
|
+
return JsonResponse(data=None, safe=False, status=405)
|
69
|
+
|
70
|
+
except MethodNotAllowed as exception:
|
71
|
+
return JsonResponse(
|
72
|
+
data={'errors': f"Method {exception.method} is not allowed"},
|
73
|
+
status=405)
|
74
|
+
|
75
|
+
except Exception as exception:
|
76
|
+
# Catch-all exception handler for API endpoints.
|
77
|
+
#
|
78
|
+
# - Always defaults to HTTP 500 with "Internal server error" unless the exception provides a more specific status code and error details.
|
79
|
+
# - Duck types to extract error information from `detail` or `message` attributes, if available.
|
80
|
+
# - Never exposes internal exception contents to end users for 5xx server errors unless settings.DEBUG is True.
|
81
|
+
# - Allows structured error payloads (string, list, or dict) without assumptions about the error format.
|
82
|
+
# - Logs exceptions fully for server-side diagnostics, distinguishing handled vs unhandled cases.
|
83
|
+
#
|
84
|
+
# This design prioritizes API security, developer debugging, and future portability across projects.
|
85
|
+
|
86
|
+
status_code = getattr(exception, 'status_code', 500)
|
87
|
+
error_contents = None
|
88
|
+
|
89
|
+
if hasattr(exception, 'detail'):
|
90
|
+
error_contents = exception.detail
|
91
|
+
elif hasattr(exception, 'message') and isinstance(exception.message, str):
|
92
|
+
error_contents = exception.message
|
93
|
+
|
94
|
+
if 400 <= status_code <= 499:
|
95
|
+
if status_code == 400:
|
96
|
+
message = 'Bad request'
|
97
|
+
elif status_code == 401:
|
98
|
+
message = 'Unauthorized'
|
99
|
+
elif status_code == 403:
|
100
|
+
message = 'Forbidden'
|
101
|
+
elif status_code == 404:
|
102
|
+
message = 'Not found'
|
103
|
+
elif status_code == 405:
|
104
|
+
message = 'Method not allowed'
|
105
|
+
elif status_code == 429:
|
106
|
+
message = 'Too many requests'
|
107
|
+
elif error_contents:
|
108
|
+
message = error_contents
|
109
|
+
else:
|
110
|
+
message = 'An error occurred'
|
111
|
+
|
112
|
+
if settings.DEBUG and error_contents:
|
113
|
+
message = error_contents
|
114
|
+
else:
|
115
|
+
status_code = 500
|
116
|
+
message = 'Internal server error'
|
117
|
+
if settings.DEBUG:
|
118
|
+
message = error_contents if error_contents else str(exception)
|
119
|
+
|
120
|
+
e_name = type(exception).__name__
|
121
|
+
if error_contents:
|
122
|
+
msg = f"Handled API exception in {endpoint_name}: {e_name}: {error_contents}"
|
123
|
+
_logger.error(msg)
|
124
|
+
|
125
|
+
else:
|
126
|
+
msg = f"Unhandled exception in {endpoint_name}: {e_name}: {exception}"
|
127
|
+
_logger.error(msg)
|
128
|
+
|
129
|
+
return JsonResponse(
|
130
|
+
data={'errors': message},
|
131
|
+
safe=False,
|
132
|
+
status=status_code,
|
133
|
+
content_type='application/json')
|
small_view_set/small_view_set.py
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
import inspect
|
2
2
|
import json
|
3
3
|
import logging
|
4
|
+
from django.http import JsonResponse
|
4
5
|
from urllib.request import Request
|
5
6
|
|
6
7
|
from .exceptions import BadRequest, MethodNotAllowed
|
@@ -15,48 +16,68 @@ class SmallViewSet:
|
|
15
16
|
|
16
17
|
def protect_create(self, request: Request):
|
17
18
|
"""
|
18
|
-
|
19
|
-
|
20
|
-
|
19
|
+
Stub for adding any custom business logic to protect the create method.
|
20
|
+
For example:
|
21
|
+
- Check if the user is authenticated
|
22
|
+
- Check if the user has validated their email
|
23
|
+
- Throttle requests
|
24
|
+
|
25
|
+
Recommended to call super().protect_create(request) in the subclass in case
|
26
|
+
this library adds logic in the future.
|
21
27
|
"""
|
22
|
-
|
23
|
-
raise MethodNotAllowed('POST')
|
28
|
+
pass
|
24
29
|
|
25
30
|
def protect_list(self, request: Request):
|
26
31
|
"""
|
27
|
-
|
28
|
-
|
29
|
-
|
32
|
+
Stub for adding any custom business logic to protect the list method.
|
33
|
+
For example:
|
34
|
+
- Check if the user is authenticated
|
35
|
+
- Check if the user has validated their email
|
36
|
+
- Throttle requests
|
37
|
+
|
38
|
+
Recommended to call super().protect_create(request) in the subclass in case
|
39
|
+
this library adds logic in the future.
|
30
40
|
"""
|
31
|
-
|
32
|
-
raise MethodNotAllowed(request.method)
|
41
|
+
pass
|
33
42
|
|
34
43
|
def protect_retrieve(self, request: Request):
|
35
44
|
"""
|
36
|
-
|
37
|
-
|
38
|
-
|
45
|
+
Stub for adding any custom business logic to protect the retrieve method.
|
46
|
+
For example:
|
47
|
+
- Check if the user is authenticated
|
48
|
+
- Check if the user has validated their email
|
49
|
+
- Throttle requests
|
50
|
+
|
51
|
+
Recommended to call super().protect_create(request) in the subclass in case
|
52
|
+
this library adds logic in the future.
|
39
53
|
"""
|
40
|
-
|
41
|
-
raise MethodNotAllowed(request.method)
|
54
|
+
pass
|
42
55
|
|
43
56
|
def protect_update(self, request: Request):
|
44
57
|
"""
|
45
|
-
|
46
|
-
|
47
|
-
|
58
|
+
Stub for adding any custom business logic to protect the update method.
|
59
|
+
For example:
|
60
|
+
- Check if the user is authenticated
|
61
|
+
- Check if the user has validated their email
|
62
|
+
- Throttle requests
|
63
|
+
|
64
|
+
Recommended to call super().protect_create(request) in the subclass in case
|
65
|
+
this library adds logic in the future.
|
48
66
|
"""
|
49
|
-
|
50
|
-
raise MethodNotAllowed(request.method)
|
67
|
+
pass
|
51
68
|
|
52
69
|
def protect_delete(self, request: Request):
|
53
70
|
"""
|
54
|
-
|
55
|
-
|
56
|
-
|
71
|
+
Stub for adding any custom business logic to protect the delete method.
|
72
|
+
For example:
|
73
|
+
- Check if the user is authenticated
|
74
|
+
- Check if the user has validated their email
|
75
|
+
- Throttle requests
|
76
|
+
|
77
|
+
Recommended to call super().protect_create(request) in the subclass in case
|
78
|
+
this library adds logic in the future.
|
57
79
|
"""
|
58
|
-
|
59
|
-
raise MethodNotAllowed(request.method)
|
80
|
+
pass
|
60
81
|
|
61
82
|
async def default_router(self, request: Request, pk=None, *args, **kwargs):
|
62
83
|
"""
|
@@ -1,9 +0,0 @@
|
|
1
|
-
small_view_set/README.md,sha256=oovLoOhBsg5wK8AwGe9iyuWXStHUo9x4fsrlb-oxHt4,1061
|
2
|
-
small_view_set/__init__.py,sha256=FoYcTj03W41-w0jC1EOv1_VD4iBxO_03fIr_plSKnp8,441
|
3
|
-
small_view_set/decorators.py,sha256=f_1K7YVvWe409UdwnX3rTTypzkV0IsKqS9oBaBICqRQ,7509
|
4
|
-
small_view_set/exceptions.py,sha256=5VaPS9m9syhag7p-gveG7Dep4DV3YQ7WjI1Sv5C_1N0,966
|
5
|
-
small_view_set/small_view_set.py,sha256=t_oRePTjjHhMjc1g3vrJtY9C5oW_dtfrhdKNA3PzU7Q,4775
|
6
|
-
django_small_view_set-0.1.4.dist-info/LICENSE,sha256=M4ZuHeiGHHuewaZyqz7tol4E6E2GMz7fF1ywNoXD1tA,1069
|
7
|
-
django_small_view_set-0.1.4.dist-info/METADATA,sha256=yxIVHh9dcAYCrw9157Q6SwGscdCNdhP3W5lLGyewmtc,3475
|
8
|
-
django_small_view_set-0.1.4.dist-info/WHEEL,sha256=d2fvjOD7sXsVzChCqf0Ty0JbHKBaLYwDbGQDwQTnJ50,88
|
9
|
-
django_small_view_set-0.1.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|