@@ -24,6 +24,9 @@ class AtomicOperationView(APIView):
24
24
#
25
25
serializer_classes : Dict = {}
26
26
27
+ sequential = True
28
+ response_data : List [Dict ] = []
29
+
27
30
# TODO: proof how to check permissions for all operations
28
31
# permission_classes = TODO
29
32
# call def check_permissions for `add` operation
@@ -89,30 +92,92 @@ def get_serializer_context(self):
89
92
def post (self , request , * args , ** kwargs ):
90
93
return self .perform_operations (request .data )
91
94
95
+ def handle_sequential (self , serializer , operation_code ):
96
+ if operation_code in ["add" , "update" , "update-relationship" ]:
97
+ serializer .is_valid (raise_exception = True )
98
+ serializer .save ()
99
+ if operation_code != "update-relationship" :
100
+ self .response_data .append (serializer .data )
101
+ else :
102
+ # remove
103
+ serializer .instance .delete ()
104
+
105
+ def perform_bulk_create (self , bulk_operation_data ):
106
+ objs = []
107
+ model_class = bulk_operation_data ["serializer_collection" ][0 ].Meta .model
108
+ for _serializer in bulk_operation_data ["serializer_collection" ]:
109
+ _serializer .is_valid (raise_exception = True )
110
+ instance = model_class (** _serializer .validated_data )
111
+ objs .append (instance )
112
+ self .response_data .append (
113
+ _serializer .__class__ (instance = instance ).data )
114
+ model_class .objects .bulk_create (
115
+ objs )
116
+
117
+ def perform_bulk_delete (self , bulk_operation_data ):
118
+ obj_ids = []
119
+ for _serializer in bulk_operation_data ["serializer_collection" ]:
120
+ obj_ids .append (_serializer .instance .pk )
121
+ self .response_data .append (_serializer .data )
122
+ bulk_operation_data ["serializer_collection" ][0 ].Meta .model .objects .filter (
123
+ pk__in = obj_ids ).delete ()
124
+
125
+ def handle_bulk (self , serializer , current_operation_code , bulk_operation_data ):
126
+ bulk_operation_data ["serializer_collection" ].append (serializer )
127
+ if bulk_operation_data ["next_operation_code" ] != current_operation_code or bulk_operation_data ["next_resource_type" ] != serializer .initial_data ["type" ]:
128
+ if current_operation_code == "add" :
129
+ self .perform_bulk_create (bulk_operation_data )
130
+ elif current_operation_code == "delete" :
131
+ self .perform_bulk_delete (bulk_operation_data )
132
+ else :
133
+ # TODO: update in bulk requires more logic cause it could be a partial update and every field differs pers instance.
134
+ # Then we can't do a bulk operation. This is only possible for instances which changes the same field(s).
135
+ # Maybe the anylsis of this takes longer than simple handling updates in sequential mode.
136
+ # For now we handle updates always in sequential mode
137
+ self .handle_sequential (
138
+ bulk_operation_data ["serializer_collection" ][0 ], current_operation_code )
139
+ bulk_operation_data ["serializer_collection" ] = []
140
+
92
141
def perform_operations (self , parsed_operations : List [Dict ]):
93
- response_data : List [Dict ] = []
142
+ self .response_data = [] # reset local response data storage
143
+
144
+ bulk_operation_data = {
145
+ "serializer_collection" : [],
146
+ "next_operation_code" : "" ,
147
+ "next_resource_type" : ""
148
+ }
149
+
94
150
with atomic ():
151
+
95
152
for idx , operation in enumerate (parsed_operations ):
96
- op_code = next (iter (operation ))
97
- obj = operation [op_code ]
98
- # TODO: collect operations of same op_code and resource type to support bulk_create | bulk_update | filter(id__in=[1,2,3]).delete()
153
+ operation_code = next (iter (operation ))
154
+ obj = operation [operation_code ]
155
+
99
156
serializer = self .get_serializer (
100
157
idx = idx ,
101
158
data = obj ,
102
- operation_code = "update" if op_code == "update-relationship" else op_code ,
159
+ operation_code = "update" if operation_code == "update-relationship" else operation_code ,
103
160
resource_type = obj ["type" ],
104
- partial = True if "update" in op_code else False
161
+ partial = True if "update" in operation_code else False
105
162
)
106
- if op_code in ["add" , "update" , "update-relationship" ]:
107
- serializer .is_valid (raise_exception = True )
108
- serializer .save ()
109
- # FIXME: check if it is just a relationship update
110
- if op_code == "update-relationship" :
111
- # relation update. No response data
112
- continue
113
- response_data .append (serializer .data )
114
- else :
115
- # remove
116
- serializer .instance .delete ()
117
163
118
- return Response (response_data , status = status .HTTP_200_OK if response_data else status .HTTP_204_NO_CONTENT )
164
+ if self .sequential :
165
+ self .handle_sequential (serializer , operation_code )
166
+ else :
167
+ is_last_iter = parsed_operations .__len__ () == idx + 1
168
+ if is_last_iter :
169
+ bulk_operation_data ["next_operation_code" ] = ""
170
+ bulk_operation_data ["next_resource_type" ] = ""
171
+ else :
172
+ next_operation = parsed_operations [idx + 1 ]
173
+ bulk_operation_data ["next_operation_code" ] = next (
174
+ iter (next_operation ))
175
+ bulk_operation_data ["next_resource_type" ] = next_operation [bulk_operation_data ["next_operation_code" ]]["type" ]
176
+
177
+ self .handle_bulk (
178
+ serializer = serializer ,
179
+ current_operation_code = operation_code ,
180
+ bulk_operation_data = bulk_operation_data
181
+ )
182
+
183
+ return Response (self .response_data , status = status .HTTP_200_OK if self .response_data else status .HTTP_204_NO_CONTENT )
0 commit comments