4     from hashlib import md5
 
   6     from md5 import new as md5
 
   7 from urllib import quote_plus, urlencode
 
   8 from django.db import models, IntegrityError, connection, transaction
 
   9 from django.utils.http import urlquote  as django_urlquote
 
  10 from django.utils.html import strip_tags
 
  11 from django.conf import settings as django_settings
 
  12 from django.core.urlresolvers import reverse
 
  13 from django.contrib.contenttypes import generic
 
  14 from django.contrib.contenttypes.models import ContentType
 
  15 from django.core.cache import cache
 
  16 from django.template.defaultfilters import slugify
 
  17 from django.db.models.signals import post_delete, post_save, pre_save, pre_delete
 
  18 from django.utils.translation import ugettext as _
 
  19 from django.utils.safestring import mark_safe
 
  20 from django.utils.encoding import force_unicode
 
  21 from django.contrib.sitemaps import ping_google
 
  22 import django.dispatch
 
  23 from forum import settings
 
  27 if not hasattr(cache, 'get_many'):
 
  28     #put django 1.2 code here
 
  31 class LazyQueryList(object):
 
  32     def __init__(self, model, items):
 
  36     def __getitem__(self, k):
 
  37         return self.model.objects.get(id=self.items[k][0])
 
  41             yield self.model.objects.get(id=id[0])
 
  44         return len(self.items)
 
  46 class ToFetch(unicode):
 
  49 class CachedQuerySet(models.query.QuerySet):
 
  52         if not len(self.query.aggregates):
 
  55             if len(self.query.extra):
 
  56                 extra_keys = self.query.extra.keys()
 
  57                 values_list += extra_keys
 
  59             return LazyQueryList(self.model, list(self.values_list(*values_list)))
 
  63     def obj_from_datadict(self, datadict):
 
  65         obj.__dict__.update(datadict)
 
  67         if hasattr(obj, '_state'):
 
  68             obj._state.db = 'default'
 
  72     def _base_clone(self):
 
  73         return self._clone(klass=models.query.QuerySet)
 
  75     def get(self, *args, **kwargs):
 
  76         key = self.model.infer_cache_key(kwargs)
 
  82                 obj = self._base_clone().get(*args, **kwargs)
 
  85                 obj = self.obj_from_datadict(obj)
 
  87             obj.reset_original_state()
 
  91         return self._base_clone().get(*args, **kwargs)
 
  93     def _fetch_from_query_cache(self, key):
 
  94         invalidation_key = self.model._get_cache_query_invalidation_key()
 
  95         cached_result = cache.get_many([invalidation_key, key])
 
  97         if not invalidation_key in cached_result:
 
  98             self.model._set_query_cache_invalidation_timestamp()
 
 101         if (key in cached_result) and(cached_result[invalidation_key] < cached_result[key][0]):
 
 102             return cached_result[key][1]
 
 107         cache_key = self.model._generate_cache_key("CNT:%s" % self._get_query_hash())
 
 108         result = self._fetch_from_query_cache(cache_key)
 
 110         if result is not None:
 
 113         result = super(CachedQuerySet, self).count()
 
 114         cache.set(cache_key, (datetime.datetime.now(), result), 60 * 60)
 
 118         cache_key = self.model._generate_cache_key("QUERY:%s" % self._get_query_hash())
 
 119         on_cache_query_attr = self.model.value_to_list_on_cache_query()
 
 124         with_aggregates = len(self.query.aggregates) > 0
 
 125         key_list = self._fetch_from_query_cache(cache_key)
 
 128             if not with_aggregates:
 
 129                 values_list = [on_cache_query_attr]
 
 131                 if len(self.query.extra):
 
 132                     values_list += self.query.extra.keys()
 
 134                 key_list = [v[0] for v in self.values_list(*values_list)]
 
 135                 to_cache[cache_key] = (datetime.datetime.now(), key_list)
 
 137                 to_return = list(super(CachedQuerySet, self).iterator())
 
 138                 to_cache[cache_key] = (datetime.datetime.now(), [
 
 139                     (row.__dict__[on_cache_query_attr], dict([(k, row.__dict__[k]) for k in self.query.aggregates.keys()]))
 
 140                     for row in to_return])
 
 141         elif with_aggregates:
 
 143             key_list = [k[0] for k in tmp]
 
 144             with_aggregates = [k[1] for k in tmp]
 
 147         if (not to_return) and key_list:
 
 148             row_keys = [self.model.infer_cache_key({on_cache_query_attr: attr}) for attr in key_list]
 
 149             cached = cache.get_many(row_keys)
 
 152                 (ck in cached) and self.obj_from_datadict(cached[ck]) or ToFetch(force_unicode(key_list[i])) for i, ck in enumerate(row_keys)
 
 155             if len(cached) != len(row_keys):
 
 156                 to_fetch = [unicode(tr) for tr in to_return if isinstance(tr, ToFetch)]
 
 158                 fetched = dict([(force_unicode(r.__dict__[on_cache_query_attr]), r) for r in
 
 159                               models.query.QuerySet(self.model).filter(**{"%s__in" % on_cache_query_attr: to_fetch})])
 
 161                 to_return = [(isinstance(tr, ToFetch) and fetched[unicode(tr)] or tr) for tr in to_return]
 
 162                 to_cache.update(dict([(self.model.infer_cache_key({on_cache_query_attr: attr}), r._as_dict()) for attr, r in fetched.items()]))
 
 165                 for i, r in enumerate(to_return):
 
 166                     r.__dict__.update(with_aggregates[i])
 
 170             cache.set_many(to_cache, 60 * 60)
 
 173             for row in to_return:
 
 174                 if hasattr(row, 'leaf'):
 
 177                 row.reset_original_state()
 
 180     def _get_query_hash(self):
 
 182             return md5(unicode(self.query).encode("utf-8")).hexdigest()
 
 184             return md5(self.query).hexdigest()        
 
 188 class CachedManager(models.Manager):
 
 189     use_for_related_fields = True
 
 191     def get_query_set(self):
 
 192         return CachedQuerySet(self.model)
 
 194     def get_or_create(self, *args, **kwargs):
 
 196             return self.get(*args, **kwargs)
 
 198             return super(CachedManager, self).get_or_create(*args, **kwargs)
 
 201 class DenormalizedField(object):
 
 202     def __init__(self, manager, *args, **kwargs):
 
 203         self.manager = manager
 
 204         self.filter = (args, kwargs)
 
 206     def setup_class(self, cls, name):
 
 207         dict_name = '_%s_dencache_' % name
 
 210             val = inst.__dict__.get(dict_name, None)
 
 213                 val = getattr(inst, self.manager).filter(*self.filter[0], **self.filter[1]).count()
 
 214                 inst.__dict__[dict_name] = val
 
 219         def reset_cache(inst):
 
 220             inst.__dict__.pop(dict_name, None)
 
 223         cls.add_to_class(name, property(getter))
 
 224         cls.add_to_class("reset_%s_cache" % name, reset_cache)
 
 227 class BaseMetaClass(models.Model.__metaclass__):
 
 230     def __new__(cls, *args, **kwargs):
 
 231         new_cls = super(BaseMetaClass, cls).__new__(cls, *args, **kwargs)
 
 233         BaseMetaClass.to_denormalize.extend(
 
 234             [(new_cls, name, field) for name, field in new_cls.__dict__.items() if isinstance(field, DenormalizedField)]
 
 240     def setup_denormalizes(cls):
 
 241         for new_cls, name, field in BaseMetaClass.to_denormalize:
 
 242             field.setup_class(new_cls, name)
 
 245 class BaseModel(models.Model):
 
 246     __metaclass__ = BaseMetaClass
 
 248     objects = CachedManager()
 
 254     def __init__(self, *args, **kwargs):
 
 255         super(BaseModel, self).__init__(*args, **kwargs)
 
 256         self.reset_original_state(kwargs.keys())
 
 258     def reset_original_state(self, reset_fields=None):
 
 259         self._original_state = self._as_dict()
 
 262             self._original_state.update(dict([(f, None) for f in reset_fields]))
 
 264     def get_dirty_fields(self):
 
 265         return [f.name for f in self._meta.fields if self._original_state[f.attname] != self.__dict__[f.attname]]
 
 268         return dict([(name, getattr(self, name)) for name in
 
 269                      ([f.attname for f in self._meta.fields] + [k for k in self.__dict__.keys() if k.endswith('_dencache_')])
 
 272     def _get_update_kwargs(self):
 
 274             (f.name, getattr(self, f.name)) for f in self._meta.fields if self._original_state[f.attname] != self.__dict__[f.attname]
 
 277     def save(self, full_save=False, *args, **kwargs):
 
 278         put_back = [k for k, v in self.__dict__.items() if isinstance(v, models.expressions.ExpressionNode)]
 
 280         if hasattr(self, '_state'):
 
 281             self._state.db = 'default'
 
 283         if self.id and not full_save:
 
 284             self.__class__.objects.filter(id=self.id).update(**self._get_update_kwargs())
 
 286             super(BaseModel, self).save()
 
 290                 self.__dict__.update(
 
 291                     self.__class__.objects.filter(id=self.id).values(*put_back)[0]
 
 294                 logging.error("Unable to read %s from %s" % (", ".join(put_back), self.__class__.__name__))
 
 297         self.reset_original_state()
 
 298         self._set_query_cache_invalidation_timestamp()
 
 302     def _get_cache_query_invalidation_key(cls):
 
 303         return cls._generate_cache_key("INV_TS")
 
 306     def _set_query_cache_invalidation_timestamp(cls):
 
 307         cache.set(cls._get_cache_query_invalidation_key(), datetime.datetime.now(), 60 * 60 * 24)
 
 309         for base in filter(lambda c: issubclass(c, BaseModel) and (not c is BaseModel), cls.__bases__):
 
 310             base._set_query_cache_invalidation_timestamp()
 
 313     def _generate_cache_key(cls, key, group=None):
 
 317         return '%s:%s:%s' % (settings.APP_URL, group, key)
 
 320         return self._generate_cache_key(self.id)
 
 323     def value_to_list_on_cache_query(cls):
 
 327     def infer_cache_key(cls, querydict):
 
 329             pk = [v for (k,v) in querydict.items() if k in ('pk', 'pk__exact', 'id', 'id__exact'
 
 330                             ) or k.endswith('_ptr__pk') or k.endswith('_ptr__id')][0]
 
 332             cache_key = cls._generate_cache_key(pk)
 
 334             if len(cache_key) > django_settings.CACHE_MAX_KEY_LENGTH:
 
 335                 cache_key = cache_key[:django_settings.CACHE_MAX_KEY_LENGTH]
 
 342         cache.set(self.cache_key(), self._as_dict(), 60 * 60)
 
 345         cache.delete(self.cache_key())
 
 349         self._set_query_cache_invalidation_timestamp()
 
 350         super(BaseModel, self).delete()
 
 353 from user import User
 
 354 from node import Node, NodeRevision, NodeManager
 
 355 from action import Action