Show
Ignore:
Timestamp:
03/28/08 08:51:04 (2 years ago)
Author:
JensDiemer
Message:

Updating the cache middleware:

  • Splitting pagestats from cache is a good idea, after all ;)
  • add a debug information in the response
  • verify the shortcut from the url
  • update unittests
Files:
1 modified

Legend:

Unmodified
Added
Removed
  • trunk/pylucid/PyLucid/middlewares/cache.py

    r1510 r1511  
    1212        - Build the cache key based on the page shortcuts 
    1313 
    14     If request.debug == True: We added set response["from_cache"] = "yes" if 
    15     the response comes from the cache. 
     14    If request.debug == True: We added added information about the cache status 
     15    in _insert_cache_info(): 
     16        - set response["from_cache"] to "yes" / "no" (Not True/False!) 
     17        - Replace the pagestats TAG with cache debug information. 
    1618 
    1719    The page statistics part bases on: 
     
    3537from django.core.cache import cache 
    3638 
     39from PyLucid.models import Page 
     40from PyLucid.tools.shortcuts import verify_shortcut 
     41from PyLucid.middlewares.pagestats import TAG 
    3742from PyLucid.template_addons.filters import human_duration 
    38 from PyLucid.models import Page 
    3943 
    4044CACHE_TIMEOUT = settings.CACHE_MIDDLEWARE_SECONDS 
    4145 
    42 # Save the start time of the current running pyhon instance 
    43 start_overall = time.time() 
    44  
    45 TAG = u"<!-- script_duration -->" 
    46  
    47 FMT = ( 
    48     u'render time: %(total_time)s -' 
    49     ' overall: %(overall_time)s -' 
    50     ' queries: %(queries)d' 
    51 ) 
     46CACHED_INFO_YES = u"from cache" 
     47CACHED_INFO_NO = u"not cached" 
    5248 
    5349# RFC1123 date format as specified by HTTP RFC2616 section 3.3.1: 
     
    7066        shortcut = url.rsplit("/", 1)[-1] 
    7167 
     68    # Check a shortcut. A AssertionError whould be raised if something seems to 
     69    # be wrong. 
     70    # Normaly the url-re of the index view filters bad things out. But 
     71    # process_request in the middleware whould becalled before this done. 
     72    verify_shortcut(shortcut) 
     73 
    7274    cache_key = settings.PAGE_CACHE_PREFIX + shortcut 
    7375    return shortcut, cache_key 
     
    7577 
    7678class CacheMiddleware(object): 
     79    def __init__(self): 
     80        self.cache_key = None 
     81 
    7782    def process_request(self, request): 
    7883        """ 
     
    8186        user makes a GET or HEAD requests. 
    8287        """ 
    83         # save start time and the number of db queries before we do anything 
    84         self.start_time = time.time() 
    85         self.old_queries = len(connection.queries) 
     88        request._from_cache = False 
    8689 
    8790        # cache only GET or HEAD requests 
     
    99102        # Build the cache key based on the page shortcuts 
    100103        url = request.path 
    101         shortcut, self.cache_key = build_cache_key(url) 
     104        try: 
     105            shortcut, self.cache_key = build_cache_key(url) 
     106        except AssertionError, e: 
     107            # Something is wrong with the given url 
     108            request._use_cache = False 
     109            return 
    102110 
    103111        # Get the page data from the cache. If not exist response is None. 
    104112        response = cache.get(self.cache_key) 
    105113 
    106         if response: 
    107             # The page data exist in the cache 
    108             assert isinstance(response, HttpResponse) 
    109             if request.debug: 
    110                 #print "Use cached page version. (key: '%s')" % self.cache_key 
    111                 response["from_cache"] = "yes" 
    112             self.insert_page_stats(request, response) 
    113         #elif settings.DEBUG: 
    114             #print "Page not in cache found. (key: '%s')" % self.cache_key 
     114        if response == None: 
     115            # The page data doesn't exist in the cache 
     116            return 
     117 
     118        # The page data exist in the cache 
     119        assert isinstance(response, HttpResponse) 
     120        request._from_cache = True 
     121 
     122        if request.debug: 
     123            # Add the cache debug information. 
     124            response = self._insert_cache_info(request, response, True) 
    115125 
    116126        return response 
     
    121131        Cache the response and insert the page statistics. 
    122132        """ 
     133        if request._from_cache == True: 
     134            # The content comes from the cache 
     135            return response 
     136 
    123137        if getattr(request, "_use_cache", False) != True: 
    124138            # Don't cache 
    125             self.insert_page_stats(request, response) 
     139            response = self._insert_cache_info(request, response, False) 
    126140            return response 
    127141 
    128142        # cache the response 
    129         self.cache_response(request, response) 
    130  
    131         self.insert_page_stats(request, response) 
     143        self._cache_response(request, response) 
     144 
     145        response = self._insert_cache_info(request, response, False) 
    132146 
    133147        return response 
    134148 
    135149 
    136     def cache_response(self, request, response): 
    137         """ 
    138         Cache the given response. 
    139         """ 
    140         # Add cache info headers to the response object 
    141         self.patch_response_headers(request, response) 
    142  
    143         # Save the page into the cache 
    144         cache.set(self.cache_key, response, CACHE_TIMEOUT) 
    145  
    146  
    147     def patch_response_headers(self, request, response): 
    148         """ 
    149         Adds some useful response headers for the browser cache to the given 
    150         HttpResponse object. 
    151         Based on django.utils.cache.patch_response_headers() but here we use 
    152         the original page last update time. 
    153         """ 
    154         # The the original page last update time 
    155         context = request.CONTEXT 
    156         current_page_obj = context["PAGE"] 
    157         lastupdatetime = current_page_obj.lastupdatetime 
    158  
    159         response['ETag'] = md5.new(self.cache_key).hexdigest() 
    160         response['Last-Modified'] = lastupdatetime.strftime(DATE_FORMAT) 
    161         now = datetime.datetime.utcnow() 
    162         expires = now + datetime.timedelta(0, CACHE_TIMEOUT) 
    163         response['Expires'] = expires.strftime(DATE_FORMAT) 
    164  
    165  
    166     def insert_page_stats(self, request, response): 
    167         """ 
    168         calculate the statistic and replace it into the html page. 
    169         """ 
    170         # Put only the statistic into HTML pages 
    171         if not "html" in response._headers["content-type"][1]: 
    172             # No HTML Page -> do nothing 
    173             return response 
    174  
    175         # compute the db time for the queries just run 
    176         # FIXME: In my shared webhosting environment the queries is always = 0 
    177         queries = len(connection.queries) - self.old_queries 
    178  
    179         total_time = human_duration(time.time() - self.start_time) 
    180         overall_time = human_duration(time.time() - start_overall) 
    181  
    182         # replace the comment if found 
    183         stat_info = FMT % { 
    184             'total_time' : total_time, 
    185             'overall_time' : overall_time, 
    186             'queries' : queries, 
    187         } 
     150    def _insert_cache_info(self, request, response, is_from_cache): 
     151        """ 
     152        Add the cache debug information. 
     153 
     154        if request.debug == True, we added the information if the response 
     155        was from the cache or not. We "added" the information after the 
     156        pagestats >TAG< ;) 
     157        """ 
     158        if request._from_cache == True: 
     159            response_msg = CACHED_INFO_YES 
     160            response["from_cache"] = "yes" 
     161        elif request._from_cache == False: 
     162            response_msg = CACHED_INFO_NO 
     163            response["from_cache"] = "no" 
     164        else: 
     165            raise AssertionError("wrong request._from_cache info.") 
    188166 
    189167        content = response.content 
     
    197175                return response 
    198176 
    199         # insert the page statistic 
    200         new_content = content.replace(TAG, stat_info) 
     177        # Replace the pagestats TAG with the cache debug information. 
     178        message = u"%s - %s - cache key: %s" % ( 
     179            TAG, response_msg, self.cache_key 
     180        ) 
     181        new_content = content.replace(TAG, message) 
    201182        response.content = new_content 
    202183 
    203184        return response 
     185 
     186 
     187    def _cache_response(self, request, response): 
     188        """ 
     189        Cache the given response. 
     190        """ 
     191        # Add cache info headers to the response object 
     192        self._patch_response_headers(request, response) 
     193 
     194        # Save the page into the cache 
     195        cache.set(self.cache_key, response, CACHE_TIMEOUT) 
     196 
     197 
     198    def _patch_response_headers(self, request, response): 
     199        """ 
     200        Adds some useful response headers for the browser cache to the given 
     201        HttpResponse object. 
     202        Based on django.utils.cache.patch_response_headers() but here we use 
     203        the original page last update time. 
     204        """ 
     205        # The the original page last update time 
     206        context = request.CONTEXT 
     207        current_page_obj = context["PAGE"] 
     208        lastupdatetime = current_page_obj.lastupdatetime 
     209 
     210        response['ETag'] = md5.new(self.cache_key).hexdigest() 
     211        response['Last-Modified'] = lastupdatetime.strftime(DATE_FORMAT) 
     212        now = datetime.datetime.utcnow() 
     213        expires = now + datetime.timedelta(0, CACHE_TIMEOUT) 
     214        response['Expires'] = expires.strftime(DATE_FORMAT)