11"""
22The main DebugToolbar class that loads and renders the Toolbar.
33"""
4-
4+ import logging
55import uuid
6- from collections import OrderedDict
76from functools import lru_cache
87
98from django .apps import apps
1716from django .utils .translation import get_language , override as lang_override
1817
1918from debug_toolbar import APP_NAME , settings as dt_settings
19+ from debug_toolbar .store import get_store
20+
21+ logger = logging .getLogger (__name__ )
2022
2123
2224class DebugToolbar :
2325 # for internal testing use only
2426 _created = Signal ()
27+ store = None
2528
26- def __init__ (self , request , get_response ):
29+ def __init__ (self , request , get_response , request_id = None ):
2730 self .request = request
2831 self .config = dt_settings .get_config ().copy ()
2932 panels = []
@@ -33,16 +36,11 @@ def __init__(self, request, get_response):
3336 if panel .enabled :
3437 get_response = panel .process_request
3538 self .process_request = get_response
36- # Use OrderedDict for the _panels attribute so that items can be efficiently
37- # removed using FIFO order in the DebugToolbar.store() method. The .popitem()
38- # method of Python's built-in dict only supports LIFO removal.
39- self ._panels = OrderedDict ()
40- while panels :
41- panel = panels .pop ()
42- self ._panels [panel .panel_id ] = panel
39+ self ._panels = {panel .panel_id : panel for panel in reversed (panels )}
4340 self .stats = {}
4441 self .server_timing_stats = {}
45- self .request_id = None
42+ self .request_id = request_id
43+ self .init_store ()
4644 self ._created .send (request , toolbar = self )
4745
4846 # Manage panels
@@ -74,7 +72,7 @@ def render_toolbar(self):
7472 Renders the overall Toolbar with panels inside.
7573 """
7674 if not self .should_render_panels ():
77- self .store ()
75+ self .init_store ()
7876 try :
7977 context = {"toolbar" : self }
8078 lang = self .config ["TOOLBAR_LANGUAGE" ] or get_language ()
@@ -106,20 +104,20 @@ def should_render_panels(self):
106104
107105 # Handle storing toolbars in memory and fetching them later on
108106
109- _store = OrderedDict ()
107+ def init_store (self ):
108+ # Store already initialized.
109+ if self .store is None :
110+ self .store = get_store ()
110111
111- def store (self ):
112- # Store already exists.
113112 if self .request_id :
114113 return
115114 self .request_id = uuid .uuid4 ().hex
116- self ._store [self .request_id ] = self
117- for _ in range (self .config ["RESULTS_CACHE_SIZE" ], len (self ._store )):
118- self ._store .popitem (last = False )
115+ self .store .set (self .request_id )
119116
120117 @classmethod
121- def fetch (cls , request_id ):
122- return cls ._store .get (request_id )
118+ def fetch (cls , request_id , panel_id = None ):
119+ if get_store ().exists (request_id ):
120+ return StoredDebugToolbar .from_store (request_id , panel_id = panel_id )
123121
124122 # Manually implement class-level caching of panel classes and url patterns
125123 # because it's more obvious than going through an abstraction.
@@ -186,3 +184,38 @@ def observe_request(request):
186184 Determine whether to update the toolbar from a client side request.
187185 """
188186 return not DebugToolbar .is_toolbar_request (request )
187+
188+
189+ def from_store_get_response (request ):
190+ logger .warning (
191+ "get_response was called for debug toolbar after being loaded from the store. No request exists in this scenario as the request is not stored, only the panel's data."
192+ )
193+ return None
194+
195+
196+ class StoredDebugToolbar (DebugToolbar ):
197+ def __init__ (self , request , get_response , request_id = None ):
198+ self .request = None
199+ self .config = dt_settings .get_config ().copy ()
200+ self .process_request = get_response
201+ self .stats = {}
202+ self .server_timing_stats = {}
203+ self .request_id = request_id
204+ self .init_store ()
205+
206+ @classmethod
207+ def from_store (cls , request_id , panel_id = None ):
208+ toolbar = StoredDebugToolbar (
209+ None , from_store_get_response , request_id = request_id
210+ )
211+ toolbar ._panels = {}
212+
213+ for panel_class in reversed (cls .get_panel_classes ()):
214+ panel = panel_class (toolbar , from_store_get_response )
215+ if panel_id and panel .panel_id != panel_id :
216+ continue
217+ data = toolbar .store .panel (toolbar .request_id , panel .panel_id )
218+ if data :
219+ panel .load_stats_from_store (data )
220+ toolbar ._panels [panel .panel_id ] = panel
221+ return toolbar
0 commit comments