Provides an efficient means of looking up multiple related model instances for a range of objects by pre-filling the cache attribute used by SingleRelatedObjectDescriptor
with either complete model instances or a dict containing only specified fields, looking up all required data with a single query.
Example usage:
C:\django_projects\soclone>django-admin.py shell
>>> from soclone.models import Question
>>> from soclone.views import populate_fk_caches
>>> from django.db import connection
>>> from django.contrib.auth.models import User
>>> q = Question.objects.get(id=1)
>>> a = list(q.answers.all())
>>> connection.queries = []
>>> populate_fk_caches(User, (
... ((q,), ('author', 'last_edited_by', 'closed_by')),
... (a, ('author', 'last_edited_by')),
... ),
... fields=('username', 'gravatar', 'reputation', 'gold', 'silver',
... 'bronze'))
>>> connection.queries
[{'time': '0.000', 'sql': u'SELECT "auth_user"."id", "auth_user"."username", "au
th_user"."gravatar", "auth_user"."reputation", "auth_user"."gold", "auth_user"."
silver", "auth_user"."bronze" FROM "auth_user" WHERE "auth_user"."id" IN (1, 2)'
}]
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | import itertools
def flatten(x):
"""flatten(sequence) -> list
Returns a single, flat list which contains all elements retrieved
from the sequence and all recursively contained sub-sequences
(iterables).
Examples:
>>> [1, 2, [3,4], (5,6)]
[1, 2, [3, 4], (5, 6)]
>>> flatten([[[1,2,3], (42,None)], [4,5], [6], 7, MyVector(8,9,10)])
[1, 2, 3, 42, None, 4, 5, 6, 7, 8, 9, 10]
http://kogs-www.informatik.uni-hamburg.de/~meine/python_tricks"""
result = []
for el in x:
if hasattr(el, '__iter__') and not isinstance(el, basestring):
result.extend(flatten(el))
else:
result.append(el)
return result
def populate_fk_caches(model, objects_to_populate, fields=None):
"""
Populates caches for the given related Model in instances of objects
which have a ForeignKey relationship to it, specified as a list of
(object list, related attribute name list) two-tuples.
If a list of field names is given, only the given fields will be
looked up and related object caches will be populated with a dict of
the specified fields. This list shouldn't include the primary key
attribute for the related mode, as this can be determined from its
Model's Options.
If field names are not given, complete related objects will be
retrieved and cached.
"""
# Get all related object ids for the appropriate fields
related_object_ids = []
for objects, attrs in objects_to_populate:
related_object_ids.append(tuple(tuple(getattr(obj, '%s_id' % attr)
for attr in attrs)
for obj in objects))
# Get unique related object ids
unique_ids = tuple(set(pk for pk in flatten(related_object_ids) if pk))
# Retrieve related object details
if fields is None:
related_objects = model._default_manager.in_bulk(unique_ids)
else:
id_attribute = model._meta.pk.attname
related_objects = dict((obj[id_attribute], obj)
for obj in model._default_manager.filter(id__in=unique_ids).values(
*itertools.chain((id_attribute,), fields)))
# Fill related object caches
for (objects, attrs), related_ids in itertools.izip(objects_to_populate,
related_object_ids):
for obj, related_ids_for_obj in itertools.izip(objects,
related_ids):
for attr, related_object in itertools.izip(attrs, (related_objects.get(pk, None)
for pk in related_ids_for_obj)):
setattr(obj, '_%s_cache' % attr, related_object)
|
More like this
- Template tag - list punctuation for a list of items by shapiromatron 11 months, 2 weeks ago
- JSONRequestMiddleware adds a .json() method to your HttpRequests by cdcarter 11 months, 3 weeks ago
- Serializer factory with Django Rest Framework by julio 1 year, 6 months ago
- Image compression before saving the new model / work with JPG, PNG by Schleidens 1 year, 7 months ago
- Help text hyperlinks by sa2812 1 year, 7 months ago
Comments
In recent versions of Django the "_%s_cache" string shouldn't be hard-coded as there's a method to get it
db/models/related.py
#
also, for the '%s_id'
#
Please login first before commenting.