44
55from django .db .models import Model
66from django .http import HttpRequest , JsonResponse
7- from django .http .response import HttpResponseRedirect
87from django .views .decorators .csrf import csrf_protect
98from django .views .decorators .http import require_POST
109
1110import orjson
1211
1312from .call_method_parser import InvalidKwarg , parse_call_method_name , parse_kwarg
14- from .components import HashUpdate , LocationUpdate , UnicornField , UnicornView
13+ from .components import UnicornField , UnicornView
1514from .errors import UnicornViewError
16- from .serializer import dumps
17- from .utils import generate_checksum
15+ from .message import ComponentRequest , Return
1816
1917
2018logger = logging .getLogger (__name__ )
@@ -180,44 +178,6 @@ def _call_method_name(
180178 return func ()
181179
182180
183- class ComponentRequest :
184- """
185- Parses, validates, and stores all of the data from the message request.
186- """
187-
188- def __init__ (self , request ):
189- self .body = {}
190-
191- try :
192- self .body = orjson .loads (request .body )
193- assert self .body , "Invalid JSON body"
194- except orjson .JSONDecodeError as e :
195- raise UnicornViewError ("Body could not be parsed" ) from e
196-
197- self .data = self .body .get ("data" )
198- assert self .data is not None , "Missing data" # data could theoretically be {}
199-
200- self .id = self .body .get ("id" )
201- assert self .id , "Missing component id"
202-
203- self .validate_checksum ()
204-
205- self .action_queue = self .body .get ("actionQueue" , [])
206-
207- def validate_checksum (self ):
208- """
209- Validates that the checksum in the request matches the data.
210-
211- Returns:
212- Raises `AssertionError` if the checksums don't match.
213- """
214- checksum = self .body .get ("checksum" )
215- assert checksum , "Missing checksum"
216-
217- generated_checksum = generate_checksum (orjson .dumps (self .data ))
218- assert checksum == generated_checksum , "Checksum does not match"
219-
220-
221181@handle_error
222182@csrf_protect
223183@require_POST
@@ -258,7 +218,7 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
258218 component .hydrate ()
259219
260220 is_reset_called = False
261- return_value = None
221+ return_data = None
262222
263223 for action in component_request .action_queue :
264224 action_type = action .get ("type" )
@@ -326,6 +286,7 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
326286 assert call_method_name , "Missing 'name' key for callMethod"
327287
328288 (method_name , params ) = parse_call_method_name (call_method_name )
289+ return_data = Return (method_name , params )
329290 setter_method = {}
330291
331292 if "=" in call_method_name :
@@ -341,9 +302,8 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
341302 property_value = setter_method [property_name ]
342303
343304 _set_property_value (component , property_name , property_value )
305+ return_data = Return (property_name , [property_value ])
344306 else :
345- (method_name , params ) = parse_call_method_name (call_method_name )
346-
347307 if method_name == "$refresh" :
348308 # Handle the refresh special action
349309 component = UnicornView .create (
@@ -375,41 +335,16 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
375335 validate_all_fields = True
376336 else :
377337 component .calling (method_name , params )
378- return_value = _call_method_name (component , method_name , params )
338+ return_data .value = _call_method_name (
339+ component , method_name , params
340+ )
379341 component .called (method_name , params )
380342 else :
381343 raise UnicornViewError (f"Unknown action_type '{ action_type } '" )
382344
383345 # Re-load frontend context variables to deal with non-serializable properties
384346 component_request .data = orjson .loads (component .get_frontend_context_variables ())
385347
386- return_data = {}
387- redirect_data = {}
388-
389- # TODO: Support a tuple/list return_value which could contain a redirect and value(s).
390- # `return (redirect(...), 1)` -> { return: { 1 } }, redirect: { url: "..." } }
391- # easier for `HashUpdate`, would need to handle setting last return value after new component loaded
392- if return_value is not None :
393- if isinstance (return_value , HttpResponseRedirect ):
394- redirect_data = {
395- "url" : return_value .url ,
396- }
397- elif isinstance (return_value , HashUpdate ):
398- redirect_data = {
399- "hash" : return_value .hash ,
400- }
401- elif isinstance (return_value , LocationUpdate ):
402- redirect_data = {
403- "url" : return_value .redirect .url ,
404- "refresh" : True ,
405- "title" : return_value .title ,
406- }
407- else :
408- try :
409- return_data = orjson .loads (dumps (return_value ))
410- except :
411- pass
412-
413348 if not is_reset_called :
414349 if validate_all_fields :
415350 component .validate ()
@@ -429,8 +364,11 @@ def message(request: HttpRequest, component_name: str = None) -> JsonResponse:
429364 "dom" : rendered_component ,
430365 "data" : component_request .data ,
431366 "errors" : component .errors ,
432- "redirect" : redirect_data ,
433- "return" : return_data ,
434367 }
435368
369+ if return_data :
370+ res .update (
371+ {"redirect" : return_data .redirect , "return" : return_data .get_data (),}
372+ )
373+
436374 return JsonResponse (res )
0 commit comments