Prefill ForeignKey caches

 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

  1. Debug middleware for displaying sql queries and template loading info when ?debug=true by SEJeff 1 year, 1 month ago
  2. FilterSpec for ForeignKeys to auth.User with HTML input tag for ModelAdmin.list_filter by loic 1 year ago
  3. Q marshaller by Spike^ekipS 4 years, 2 months ago
  4. Finding related objects for instances in a queryset by akaihola 1 year, 2 months ago
  5. Limit ForeignKey filter values to those that have a relationship with current model by overclocked 1 year, 6 months ago

Comments

anentropic (on August 24, 2011):

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

class RelatedObject(object):
    get_cache_name()

#

anentropic (on August 24, 2011):

also, for the '%s_id'

class RelatedObject(object):
    get_attname()

#

(Forgotten your password?)