-
Notifications
You must be signed in to change notification settings - Fork 4
/
django_superbulk.py
129 lines (107 loc) · 3.78 KB
/
django_superbulk.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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
from __future__ import absolute_import
from copy import copy
import json
from urlparse import urlparse
from django.core.urlresolvers import resolve
from django.db import transaction
from django.http import HttpResponse, QueryDict
from django.views.decorators.http import require_http_methods
class MultipleHTTPError(Exception):
"""Raised when transaction fails
message: json
"""
@require_http_methods(["POST"])
def superbulk_transactional(request):
"""Executes all transactions in the request atomically
wraps superbulk_atom call in a transaction block
@return: json formed response
"""
try:
with transaction.commit_on_success():
return superbulk_atom(request)
except MultipleHTTPError as e:
return HttpResponse(str(e), content_type='application/json')
def failfast_jsonloads(body):
"""Loads request body based on format
in order to extract failfast variable,
and preserve back compatibility.
@params: body : string
@return: tuple(bool, list)
"""
data = json.loads(body)
if isinstance(data, list):
failfast = False
elif isinstance(data, dict):
failfast = data.get('failfast', False)
data = data['content']
else:
raise NotImplementedError("Individual requests have body "
"of unsupported json type(other than list and dict)")
return failfast, data
def request_handler(request, data_list, failfast=False):
"""Handles a list of requests in json form
@params: request: Passed in from the view that calls
the method
data_list: list of the json form requests
failfast: bool var that determines if this
will fail on first error
@return: bool : if one transaction failed
list : list of request responses
"""
res_list = []
one_failed = False
for data in data_list:
if failfast and one_failed:
break
uri = urlparse(data['uri'])
view, args, kwargs = resolve(uri.path)
this_request = copy(request)
this_request._body = data['body']
this_request.method = data['method']
this_request.GET = QueryDict(uri.query)
kwargs['request'] = this_request
res = None
try:
res = view(*args, **kwargs)
except:
one_failed = True
if res is not None:
if res.status_code >= 400:
one_failed = True
res_list.append({
'status_code': res.status_code,
'headers': res._headers,
'content': res.content
})
else:
res_list.append({
'status_code': 500,
'content': json.dumps({
'reason': 'View instantiation failed'
})
})
return one_failed, res_list
def superbulk_atom(request):
"""Performs transactions in request atomically
@return: json encoded response
@raises: MultipleHttpError
"""
encoder = json.JSONEncoder()
failfast, data_list = failfast_jsonloads(request.body)
one_failed, res_list = request_handler(request, data_list, failfast=failfast)
if one_failed:
raise MultipleHTTPError(encoder.encode(res_list))
return HttpResponse(
encoder.encode(res_list), content_type='application/json')
@require_http_methods(["POST"])
def superbulk(request):
"""Performs multiple transactions passed in
from request
@return: list of results from executing
transactions
"""
encoder = json.JSONEncoder()
data_list = json.loads(request.body)
one_failed, res_list = request_handler(request, data_list)
return HttpResponse(
encoder.encode(res_list), content_type='application/json')