1 from django.db import models
2 from django.core.cache import cache
3 from django.conf import settings
4 from django.utils.encoding import force_unicode
7 from cPickle import loads, dumps
9 from pickle import loads, dumps
11 from copy import deepcopy
12 from base64 import b64encode, b64decode
13 from zlib import compress, decompress
16 from base import BaseModel
18 MAX_MARKABLE_STRING_LENGTH = 100
20 class PickledObject(unicode):
23 def dbsafe_encode(value, compress_object=True):
24 if not compress_object:
25 value = b64encode(dumps(deepcopy(value)))
27 value = b64encode(compress(dumps(deepcopy(value))))
28 return PickledObject(value)
30 def dbsafe_decode(value, compress_object=True):
31 if not compress_object:
32 value = loads(b64decode(value))
34 value = loads(decompress(b64decode(value)))
37 class PickledObjectField(models.Field):
38 __metaclass__ = models.SubfieldBase
40 marker_re = re.compile(r'^T\[(?P<type>\w+)\](?P<value>.*)$', re.DOTALL)
41 markable_types = dict((t.__name__, t) for t in (str, int, unicode))
43 def __init__(self, *args, **kwargs):
44 self.compress = kwargs.pop('compress', True)
45 self.protocol = kwargs.pop('protocol', 2)
46 kwargs.setdefault('null', True)
47 kwargs.setdefault('editable', False)
48 super(PickledObjectField, self).__init__(*args, **kwargs)
50 def generate_type_marked_value(self, value):
51 return PickledObject(u"T[%s]%s" % (type(value).__name__, value))
53 def read_marked_value(self, value):
54 m = self.marker_re.match(value)
57 marker = m.group('type')
58 value = m.group('value')
59 if marker in self.markable_types:
60 value = self.markable_types[marker](value)
64 def get_default(self):
65 if self.has_default():
66 if callable(self.default):
70 return super(PickledObjectField, self).get_default()
72 def to_python(self, value):
75 if value.startswith("T["):
76 value = self.read_marked_value(value)
78 value = dbsafe_decode(value, self.compress)
80 if isinstance(value, PickledObject):
84 def get_db_prep_value(self, value):
85 if value is not None and not isinstance(value, PickledObject):
86 if type(value).__name__ in self.markable_types and not (isinstance(value, basestring) and len(value
87 ) > MAX_MARKABLE_STRING_LENGTH):
88 value = unicode(self.generate_type_marked_value(value))
90 value = unicode(dbsafe_encode(value, self.compress))
93 def value_to_string(self, obj):
94 value = self._get_val_from_obj(obj)
95 return self.get_db_prep_value(value)
97 def get_internal_type(self):
100 def get_db_prep_lookup(self, lookup_type, value):
101 if lookup_type not in ['exact', 'in', 'isnull']:
102 raise TypeError('Lookup type %s is not supported.' % lookup_type)
103 return super(PickledObjectField, self).get_db_prep_lookup(lookup_type, value)
106 class KeyValue(BaseModel):
107 key = models.CharField(max_length=255, unique=True)
108 value = PickledObjectField()
114 return self._generate_cache_key(self.key)
117 def infer_cache_key(cls, querydict):
119 key = [v for (k, v) in querydict.items() if k in ('key', 'key__exact')][0]
121 return cls._generate_cache_key(key)