77from django .core .cache import caches
88from django .db .models import Model
99from django .http import HttpRequest , JsonResponse
10+ from django .http .response import HttpResponseNotModified
1011from django .views .decorators .csrf import csrf_protect
1112from django .views .decorators .http import require_POST
1213
1718from .call_method_parser import InvalidKwarg , parse_call_method_name , parse_kwarg
1819from .components import UnicornField , UnicornView
1920from .decorators import timed
20- from .errors import UnicornCacheError , UnicornViewError
21+ from .errors import RenderNotModified , UnicornCacheError , UnicornViewError
2122from .message import ComponentRequest , Return
2223from .serializer import dumps , loads
2324from .settings import get_cache_alias , get_serial_enabled , get_serial_timeout
@@ -41,6 +42,8 @@ def wrapped_view(*args, **kwargs):
4142 return view_func (* args , ** kwargs )
4243 except UnicornViewError as e :
4344 return JsonResponse ({"error" : str (e )})
45+ except RenderNotModified :
46+ return HttpResponseNotModified ()
4447 except AssertionError as e :
4548 return JsonResponse ({"error" : str (e )})
4649
@@ -569,7 +572,18 @@ def _process_component_request(
569572 if partial_doms :
570573 res .update ({"partials" : partial_doms })
571574 else :
572- res .update ({"dom" : rendered_component })
575+ hash = generate_checksum (rendered_component )
576+
577+ if (
578+ component_request .hash == hash
579+ and (not return_data or not return_data .value )
580+ and not component .calls
581+ ):
582+ raise RenderNotModified ()
583+
584+ res .update (
585+ {"dom" : rendered_component , "hash" : hash ,}
586+ )
573587
574588 if return_data :
575589 res .update (
@@ -714,20 +728,22 @@ def _handle_queued_component_requests(
714728 component_requests = sorted (component_requests , key = lambda r : r .epoch )
715729 first_component_request = component_requests [0 ]
716730
717- # Can't store request on a `ComponentRequest` and cache it because `HttpRequest`
718- # isn't pickleable. Does it matter that a different request gets passed in then
719- # the original request that generated the `ComponentRequest`?
720- first_json_result = _process_component_request (request , first_component_request )
721-
722- # Re-check for requests after the first request is processed
723- component_requests = cache .get (queue_cache_key )
724-
725- # Check that the request is in the cache before popping it off
726- if component_requests :
727- component_requests .pop (0 )
728- cache .set (
729- queue_cache_key , component_requests , timeout = get_serial_timeout (),
730- )
731+ try :
732+ # Can't store request on a `ComponentRequest` and cache it because `HttpRequest` isn't pickleable
733+ first_json_result = _process_component_request (request , first_component_request )
734+ except RenderNotModified :
735+ # Catching this and re-raising, but need the finally clause to clear the cache
736+ raise
737+ finally :
738+ # Re-check for requests after the first request is processed
739+ component_requests = cache .get (queue_cache_key )
740+
741+ # Check that the request is in the cache before popping it off
742+ if component_requests :
743+ component_requests .pop (0 )
744+ cache .set (
745+ queue_cache_key , component_requests , timeout = get_serial_timeout (),
746+ )
731747
732748 if component_requests :
733749 # Create one new `component_request` from all of the queued requests that can be processed
0 commit comments