1
2
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
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
31 if klass not in self.keys():
32 self[klass] = {}
33
34 return self[klass]
35
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
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
65 """
66 """
67
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
109
111 if value is fields.NOT_PROVIDED:
112 return pyamf.Undefined
113
114 if value is None:
115 return value
116
117
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
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
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
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
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
195
196
197
198
199
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
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
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
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
277
278 pyamf.register_alias_type(DjangoClassAlias, Model)
279
280
281 imports.when_imported('pyamf.amf0', install_django_reference_model_hook)
282 imports.when_imported('pyamf.amf3', install_django_reference_model_hook)
283