]> git.openstreetmap.org Git - osqa.git/blobdiff - forum/models/base.py
OSQA-776, limit the cache key length in the infer_cache_key method, adding a setting...
[osqa.git] / forum / models / base.py
index 260910635c2291afaa5a16a83d14048670034d34..33bf0c8dfbb4afe54496fae6958a62d1b71b46a7 100644 (file)
@@ -8,6 +8,7 @@ from urllib import quote_plus, urlencode
 from django.db import models, IntegrityError, connection, transaction
 from django.utils.http import urlquote  as django_urlquote
 from django.utils.html import strip_tags
+from django.conf import settings as django_settings
 from django.core.urlresolvers import reverse
 from django.contrib.contenttypes import generic
 from django.contrib.contenttypes.models import ContentType
@@ -16,6 +17,7 @@ from django.template.defaultfilters import slugify
 from django.db.models.signals import post_delete, post_save, pre_save, pre_delete
 from django.utils.translation import ugettext as _
 from django.utils.safestring import mark_safe
+from django.utils.encoding import force_unicode
 from django.contrib.sitemaps import ping_google
 import django.dispatch
 from forum import settings
@@ -41,7 +43,7 @@ class LazyQueryList(object):
     def __len__(self):
         return len(self.items)
 
-class ToFetch(str):
+class ToFetch(unicode):
     pass
 
 class CachedQuerySet(models.query.QuerySet):
@@ -118,10 +120,11 @@ class CachedQuerySet(models.query.QuerySet):
         to_return = None
         to_cache = {}
 
+        with_aggregates = len(self.query.aggregates) > 0
         key_list = self._fetch_from_query_cache(cache_key)
 
         if key_list is None:
-            if not len(self.query.aggregates):
+            if not with_aggregates:
                 values_list = [on_cache_query_attr]
 
                 if len(self.query.extra):
@@ -131,25 +134,36 @@ class CachedQuerySet(models.query.QuerySet):
                 to_cache[cache_key] = (datetime.datetime.now(), key_list)
             else:
                 to_return = list(super(CachedQuerySet, self).iterator())
-                to_cache[cache_key] = (datetime.datetime.now(), [row.__dict__[on_cache_query_attr] for row in to_return])
+                to_cache[cache_key] = (datetime.datetime.now(), [
+                    (row.__dict__[on_cache_query_attr], dict([(k, row.__dict__[k]) for k in self.query.aggregates.keys()]))
+                    for row in to_return])
+        elif with_aggregates:
+            tmp = key_list
+            key_list = [k[0] for k in tmp]
+            with_aggregates = [k[1] for k in tmp]
+            del tmp
 
         if (not to_return) and key_list:
             row_keys = [self.model.infer_cache_key({on_cache_query_attr: attr}) for attr in key_list]
             cached = cache.get_many(row_keys)
 
             to_return = [
-                (ck in cached) and self.obj_from_datadict(cached[ck]) or ToFetch(key_list[i]) for i, ck in enumerate(row_keys)
+                (ck in cached) and self.obj_from_datadict(cached[ck]) or ToFetch(force_unicode(key_list[i])) for i, ck in enumerate(row_keys)
             ]
 
             if len(cached) != len(row_keys):
-                to_fetch = [str(tr) for tr in to_return if isinstance(tr, ToFetch)]
+                to_fetch = [unicode(tr) for tr in to_return if isinstance(tr, ToFetch)]
 
-                fetched = dict([(str(r.__dict__[on_cache_query_attr]), r) for r in
+                fetched = dict([(force_unicode(r.__dict__[on_cache_query_attr]), r) for r in
                               models.query.QuerySet(self.model).filter(**{"%s__in" % on_cache_query_attr: to_fetch})])
 
-                to_return = [(isinstance(tr, ToFetch) and fetched[str(tr)] or tr) for tr in to_return]
+                to_return = [(isinstance(tr, ToFetch) and fetched[unicode(tr)] or tr) for tr in to_return]
                 to_cache.update(dict([(self.model.infer_cache_key({on_cache_query_attr: attr}), r._as_dict()) for attr, r in fetched.items()]))
 
+            if with_aggregates:
+                for i, r in enumerate(to_return):
+                    r.__dict__.update(with_aggregates[i])
+
 
         if len(to_cache):
             cache.set_many(to_cache, 60 * 60)
@@ -162,7 +176,7 @@ class CachedQuerySet(models.query.QuerySet):
                     yield row
 
     def _get_query_hash(self):
-        return md5(str(self.query)).hexdigest()
+        return md5(unicode(self.query).encode("utf-8")).hexdigest()
 
 
 
@@ -310,7 +324,12 @@ class BaseModel(models.Model):
             pk = [v for (k,v) in querydict.items() if k in ('pk', 'pk__exact', 'id', 'id__exact'
                             ) or k.endswith('_ptr__pk') or k.endswith('_ptr__id')][0]
 
-            return cls._generate_cache_key(pk)
+            cache_key = cls._generate_cache_key(pk)
+
+            if len(cache_key) > django_settings.CACHE_MAX_KEY_LENGTH:
+                cache_key = cache_key[:django_settings.CACHE_MAX_KEY_LENGTH]
+
+            return cache_key
         except:
             return None