Package pyamf :: Package adapters :: Module _django_db_models_base
[hide private]
[frames] | no frames]

Source Code for Module pyamf.adapters._django_db_models_base

  1  # Copyright (c) 2007-2009 The PyAMF Project. 
  2  # See LICENSE.txt for details. 
  3   
  4  """ 
  5  C{django.db.models} adapter module. 
  6   
  7  @see: U{Django Project<http://www.djangoproject.com>} 
  8   
  9  @since: 0.4.1 
 10  """ 
 11   
 12  from django.db.models.base import Model 
 13  from django.db.models import fields 
 14  from django.db.models.fields import related, files 
 15   
 16  import datetime 
 17   
 18  import pyamf 
 19  from pyamf.util import imports 
 20   
 21   
22 -class DjangoReferenceCollection(dict):
23 """ 24 This helper class holds a dict of klass to pk/objects loaded from the 25 underlying db. 26 27 @since: 0.5 28 """ 29
30 - def _getClass(self, klass):
31 if klass not in self.keys(): 32 self[klass] = {} 33 34 return self[klass]
35
36 - def getClassKey(self, klass, key):
37 """ 38 Return an instance based on klass/key. 39 40 If an instance cannot be found then L{KeyError} is raised. 41 42 @param klass: The class of the instance. 43 @param key: The primary_key of the instance. 44 @return: The instance linked to the C{klass}/C{key}. 45 @rtype: Instance of L{klass}. 46 """ 47 d = self._getClass(klass) 48 49 return d[key]
50
51 - def addClassKey(self, klass, key, obj):
52 """ 53 Adds an object to the collection, based on klass and key. 54 55 @param klass: The class of the object. 56 @param key: The datastore key of the object. 57 @param obj: The loaded instance from the datastore. 58 """ 59 d = self._getClass(klass) 60 61 d[key] = obj
62 63
64 -class DjangoClassAlias(pyamf.ClassAlias):
65 """ 66 """ 67
68 - def getCustomProperties(self):
69 self.fields = {} 70 self.relations = {} 71 self.columns = [] 72 73 self.meta = self.klass._meta 74 75 for x in self.meta.local_fields: 76 if isinstance(x, files.FileField): 77 self.readonly_attrs.update([x.name]) 78 79 if not isinstance(x, related.ForeignKey): 80 self.fields[x.name] = x 81 else: 82 self.relations[x.name] = x 83 84 self.columns.append(x.attname) 85 86 for k, v in self.klass.__dict__.iteritems(): 87 if isinstance(v, related.ReverseManyRelatedObjectsDescriptor): 88 self.fields[k] = v.field 89 90 parent_fields = [] 91 92 for field in self.meta.parents.values(): 93 parent_fields.append(field.attname) 94 del self.relations[field.name] 95 96 self.exclude_attrs.update(parent_fields) 97 98 props = self.fields.keys() 99 100 self.static_attrs.update(props) 101 self.encodable_properties.update(props) 102 self.decodable_properties.update(props)
103
104 - def _compile_base_class(self, klass):
105 if klass is Model: 106 return 107 108 pyamf.ClassAlias._compile_base_class(self, klass)
109
110 - def _encodeValue(self, field, value):
111 if value is fields.NOT_PROVIDED: 112 return pyamf.Undefined 113 114 if value is None: 115 return value 116 117 # deal with dates .. 118 if isinstance(field, fields.DateTimeField): 119 return value 120 elif isinstance(field, fields.DateField): 121 return datetime.datetime(value.year, value.month, value.day, 0, 0, 0) 122 elif isinstance(field, fields.TimeField): 123 return datetime.datetime(1970, 1, 1, 124 value.hour, value.minute, value.second, value.microsecond) 125 elif isinstance(value, files.FieldFile): 126 return value.name 127 128 return value
129
130 - def _decodeValue(self, field, value):
131 if value is pyamf.Undefined: 132 return fields.NOT_PROVIDED 133 134 if isinstance(field, fields.AutoField) and value == 0: 135 return None 136 elif isinstance(field, fields.DateTimeField): 137 # deal with dates 138 return value 139 elif isinstance(field, fields.DateField): 140 if not value: 141 return None 142 143 return datetime.date(value.year, value.month, value.day) 144 elif isinstance(field, fields.TimeField): 145 if not value: 146 return None 147 148 return datetime.time(value.hour, value.minute, value.second, value.microsecond) 149 150 return value
151
152 - def getEncodableAttributes(self, obj, **kwargs):
153 sa, da = pyamf.ClassAlias.getEncodableAttributes(self, obj, **kwargs) 154 155 for name, prop in self.fields.iteritems(): 156 if name not in sa: 157 continue 158 159 if isinstance(prop, related.ManyToManyField): 160 sa[name] = [x for x in getattr(obj, name).all()] 161 else: 162 sa[name] = self._encodeValue(prop, getattr(obj, name)) 163 164 if not da: 165 da = {} 166 167 keys = da.keys() 168 169 for key in keys: 170 if key.startswith('_'): 171 del da[key] 172 elif key in self.columns: 173 del da[key] 174 175 for name, relation in self.relations.iteritems(): 176 if '_%s_cache' % name in obj.__dict__: 177 da[name] = getattr(obj, name) 178 else: 179 da[name] = pyamf.Undefined 180 181 if not da: 182 da = None 183 184 return sa, da
185
186 - def getDecodableAttributes(self, obj, attrs, **kwargs):
187 attrs = pyamf.ClassAlias.getDecodableAttributes(self, obj, attrs, **kwargs) 188 189 for n in self.decodable_properties: 190 f = self.fields[n] 191 192 attrs[f.attname] = self._decodeValue(f, attrs[n]) 193 194 # primary key of django object must always be set first for 195 # relationships with other model objects to work properly 196 # and dict.iteritems() does not guarantee order 197 # 198 # django also forces the use only one attribute as primary key, so 199 # our obj._meta.pk.attname check is sufficient) 200 try: 201 setattr(obj, obj._meta.pk.attname, attrs[obj._meta.pk.attname]) 202 del attrs[obj._meta.pk.attname] 203 except KeyError: 204 pass 205 206 return attrs
207 208
209 -def getDjangoObjects(context):
210 """ 211 Returns a reference to the C{django_objects} on the context. If it doesn't 212 exist then it is created. 213 214 @param context: The context to load the C{django_objects} index from. 215 @type context: Instance of L{pyamf.BaseContext} 216 @return: The C{django_objects} index reference. 217 @rtype: Instance of L{DjangoReferenceCollection} 218 @since: 0.5 219 """ 220 if not hasattr(context, 'django_objects'): 221 context.django_objects = DjangoReferenceCollection() 222 223 return context.django_objects
224 225
226 -def writeDjangoObject(self, obj, *args, **kwargs):
227 """ 228 The Django ORM creates new instances of objects for each db request. 229 This is a problem for PyAMF as it uses the id(obj) of the object to do 230 reference checking. 231 232 We could just ignore the problem, but the objects are conceptually the 233 same so the effort should be made to attempt to resolve references for a 234 given object graph. 235 236 We create a new map on the encoder context object which contains a dict of 237 C{object.__class__: {key1: object1, key2: object2, .., keyn: objectn}}. We 238 use the primary key to do the reference checking. 239 240 @since: 0.5 241 """ 242 if not isinstance(obj, Model): 243 self.writeNonDjangoObject(obj, *args, **kwargs) 244 245 return 246 247 context = self.context 248 kls = obj.__class__ 249 250 s = obj.pk 251 252 django_objects = getDjangoObjects(context) 253 254 try: 255 referenced_object = django_objects.getClassKey(kls, s) 256 except KeyError: 257 referenced_object = obj 258 django_objects.addClassKey(kls, s, obj) 259 260 self.writeNonDjangoObject(referenced_object, *args, **kwargs)
261 262
263 -def install_django_reference_model_hook(mod):
264 """ 265 Called when L{pyamf.amf0} or L{pyamf.amf3} are imported. Attaches the 266 L{writeDjangoObject} method to the C{Encoder} class in that module. 267 268 @param mod: The module imported. 269 @since: 0.4.1 270 """ 271 if not hasattr(mod.Encoder, 'writeNonDjangoObject'): 272 mod.Encoder.writeNonDjangoObject = mod.Encoder.writeObject 273 mod.Encoder.writeObject = writeDjangoObject
274 275 276 # initialise the module here: hook into pyamf 277 278 pyamf.register_alias_type(DjangoClassAlias, Model) 279 280 # hook the L{writeDjangobject} method to the Encoder class on import 281 imports.when_imported('pyamf.amf0', install_django_reference_model_hook) 282 imports.when_imported('pyamf.amf3', install_django_reference_model_hook) 283