Skip to content

Instantly share code, notes, and snippets.

@andir
Created January 13, 2014 11:47
Show Gist options
  • Save andir/8398978 to your computer and use it in GitHub Desktop.
Save andir/8398978 to your computer and use it in GitHub Desktop.
flask-restless to_dict sqlalchemy inspection method
[3.315706968307495, 3.330582857131958, 3.3374509811401367]
Timer unit: 1e-06 s
File: [..]/flask_restless/helpers.py
Function: to_dict at line 237
Total time: 6.60634 s
Line # Hits Time Per Hit % Time Line Contents
==============================================================
237 def to_dict(instance, deep=None, exclude=None, include=None,
238 exclude_relations=None, include_relations=None,
239 include_methods=None):
240 """Returns a dictionary representing the fields of the specified `instance`
241 of a SQLAlchemy model.
242
243 The returned dictionary is suitable as an argument to
244 :func:`flask.jsonify`; :class:`datetime.date` and :class:`uuid.UUID`
245 objects are converted to string representations, so no special JSON encoder
246 behavior is required.
247
248 `deep` is a dictionary containing a mapping from a relation name (for a
249 relation of `instance`) to either a list or a dictionary. This is a
250 recursive structure which represents the `deep` argument when calling
251 :func:`!_to_dict` on related instances. When an empty list is encountered,
252 :func:`!_to_dict` returns a list of the string representations of the
253 related instances.
254
255 If either `include` or `exclude` is not ``None``, exactly one of them must
256 be specified. If both are not ``None``, then this function will raise a
257 :exc:`ValueError`. `exclude` must be a list of strings specifying the
258 columns which will *not* be present in the returned dictionary
259 representation of the object (in other words, it is a
260 blacklist). Similarly, `include` specifies the only columns which will be
261 present in the returned dictionary (in other words, it is a whitelist).
262
263 .. note::
264
265 If `include` is an iterable of length zero (like the empty tuple or the
266 empty list), then the returned dictionary will be empty. If `include` is
267 ``None``, then the returned dictionary will include all columns not
268 excluded by `exclude`.
269
270 `include_relations` is a dictionary mapping strings representing relation
271 fields on the specified `instance` to a list of strings representing the
272 names of fields on the related model which should be included in the
273 returned dictionary; `exclude_relations` is similar.
274
275 `include_methods` is a list mapping strings to method names which will
276 be called and their return values added to the returned dictionary.
277
278 """
279 30300 66252 2.2 1.0 if (exclude is not None or exclude_relations is not None) and \
280 (include is not None or include_relations is not None):
281 raise ValueError('Cannot specify both include and exclude.')
282 # create a list of names of columns, including hybrid properties
283 30300 59505 2.0 0.9 try:
284 151200 597344 4.0 9.0 columns = [p.key for p in object_mapper(instance).iterate_properties
285 120900 292212 2.4 4.4 if isinstance(p, ColumnProperty)]
286 except UnmappedInstanceError:
287 return instance
288 181800 801350 4.4 12.1 columns += [ x.__name__ for x in sqlalchemy_inspect(instance.__class__).all_orm_descriptors if
289 151500 339713 2.2 5.1 x.extension_type == hybrid.HYBRID_PROPERTY ]
290 #for parent in type(instance).mro():
291 # columns += [key for key, value in parent.__dict__.items()
292 # if isinstance(value, hybrid_property)]
293 # filter the columns based on exclude and include values
294 30300 62787 2.1 1.0 if exclude is not None:
295 columns = (c for c in columns if c not in exclude)
296 30300 60372 2.0 0.9 elif include is not None:
297 columns = (c for c in columns if c in include)
298 # create a dictionary mapping column name to value
299 30300 615233 20.3 9.3 result = dict((col, getattr(instance, col)) for col in columns
300 if not (col.startswith('__') or col in COLUMN_BLACKLIST))
301 # add any included methods
302 30300 68745 2.3 1.0 if include_methods is not None:
303 result.update(dict((method, getattr(instance, method)())
304 for method in include_methods
305 if not '.' in method))
306 # Check for objects in the dictionary that may not be serializable by
307 # default. Specifically, convert datetime and date objects to ISO 8601
308 # format, and convert UUID objects to hexadecimal strings.
309 121200 290594 2.4 4.4 for key, value in result.items():
310 # TODO We can get rid of this when issue #33 is resolved.
311 90900 247336 2.7 3.7 if isinstance(value, datetime.date):
312 result[key] = value.isoformat()
313 90900 228062 2.5 3.5 elif isinstance(value, uuid.UUID):
314 result[key] = str(value)
315 90900 2570737 28.3 38.9 elif is_mapped_class(type(value)):
316 result[key] = to_dict(value)
317 # recursively call _to_dict on each of the `deep` relations
318 30300 65728 2.2 1.0 deep = deep or {}
319 30600 79231 2.6 1.2 for relation, rdeep in deep.items():
320 # Get the related value so we can see if it is None, a list, a query
321 # (as specified by a dynamic relationship loader), or an actual
322 # instance of a model.
323 300 12220 40.7 0.2 relatedvalue = getattr(instance, relation)
324 300 651 2.2 0.0 if relatedvalue is None:
325 result[relation] = None
326 continue
327 # Determine the included and excluded fields for the related model.
328 300 573 1.9 0.0 newexclude = None
329 300 575 1.9 0.0 newinclude = None
330 300 620 2.1 0.0 if exclude_relations is not None and relation in exclude_relations:
331 newexclude = exclude_relations[relation]
332 300 614 2.0 0.0 elif (include_relations is not None and
333 relation in include_relations):
334 newinclude = include_relations[relation]
335 # Determine the included methods for the related model.
336 300 584 1.9 0.0 newmethods = None
337 300 601 2.0 0.0 if include_methods is not None:
338 newmethods = [method.split('.', 1)[1] for method in include_methods
339 if method.split('.', 1)[0] == relation]
340 300 1804 6.0 0.0 if is_like_list(instance, relation):
341 300 644 2.1 0.0 result[relation] = [to_dict(inst, rdeep, exclude=newexclude,
342 include=newinclude,
343 include_methods=newmethods)
344 30300 81880 2.7 1.2 for inst in relatedvalue]
345 300 585 1.9 0.0 continue
346 # If the related value is dynamically loaded, resolve the query to get
347 # the single instance.
348 if isinstance(relatedvalue, Query):
349 relatedvalue = relatedvalue.one()
350 result[relation] = to_dict(relatedvalue, rdeep, exclude=newexclude,
351 include=newinclude,
352 include_methods=newmethods)
353 30300 59790 2.0 0.9 return result
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment