-
Notifications
You must be signed in to change notification settings - Fork 0
/
views.py
114 lines (93 loc) · 4.2 KB
/
views.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
"""
Module contains common view mixins.
"""
import logging
try:
from http.client import responses
except ImportError:
from httplib import responses
from django.conf import settings
from django.core.exceptions import ImproperlyConfigured, PermissionDenied
from django.http import Http404, HttpResponse, JsonResponse
from .encoder import AdvancedJSONEncoder
class APIViewMixIn(object):
"""
A mixin for views that should return a JSON response.
"""
DEBUG = settings.DEBUG
encoder = AdvancedJSONEncoder
logger = logging.getLogger(__name__)
@classmethod
def evaluate(cls, data=None, success=True, message=None, code=None):
"""
Evaluate and validate the given parameters and return suitable defaults for missing ones.
"""
try:
assert (data is None or isinstance(data, dict)), 'data should be a dictionary'
assert isinstance(success, bool), 'success should be a boolean'
assert code is None or isinstance(code, int), 'code should be an integer'
except AssertionError as error:
return cls.server_error(
'A view inheriting from %s should return a tuple consisting of a data dictionary, '
'a status code integer and a boolean success flag. %s did not: %s.',
APIViewMixIn.__name__, cls.__name__, error
)
data = data or {}
code = code or {True: 200, False: 400}.get(success)
message = getattr(message, 'message', message) or responses.get(code, message)
return data, success, message, code
def dispatch(self, request, *args, **kwargs):
"""
Processes the views response and returns a JSON response if possible.
"""
try:
response = super(APIViewMixIn, self).dispatch(request, *args, **kwargs)
except PermissionDenied as error:
data, success, message, code = self.evaluate(success=False, message=f'{error}', code=403)
except Http404 as error:
data, success, message, code = self.evaluate(success=False, message=f'{error}', code=404)
except NotImplementedError:
return self.http_method_not_allowed(request, *args, **kwargs)
except Exception as error:
data, success, message, code = self.server_error('Internal Server Error: %s', error, error=error)
else:
if response is None:
data, success, message, code = self.evaluate(data=response)
elif isinstance(response, bool):
data, success, message, code = self.evaluate(success=response)
elif isinstance(response, int) and 100 <= response < 700:
data, success, message, code = self.evaluate(success=response < 400, code=response)
elif isinstance(response, tuple):
data, success, message, code = self.evaluate(*response)
elif isinstance(response, dict):
data, success, message, code = self.evaluate(data=response)
elif isinstance(response, HttpResponse):
return response
else:
data, success, message, code = self.server_error(
'A view inheriting from %s should return a tuple, a dict or a HttpResponse. %s did return %s.',
APIViewMixIn.__name__, self.__class__.__name__, response.__class__.__name__
)
return self.respond(data, success, message, code)
def respond(self, data, success, message, code):
"""
Returns given data as a JSON response and merges in the success status and the message.
"""
return JsonResponse(
data=dict({'success': success, 'message': message}, **data),
encoder=self.encoder,
status=code,
)
@classmethod
def server_error(cls, msg, *args, error=None):
if cls.DEBUG:
if error:
raise
else:
raise ImproperlyConfigured(msg % args)
if error:
cls.logger.exception(msg, *args, exc_info=error)
else:
cls.logger.error(msg, *args)
return cls.evaluate(success=False, message=f'{error}', code=500)
APIViewMixin = APIViewMixIn