2
2
Classes allowing "generic" relations through ContentType and object-id fields.
5
from collections import defaultdict
6
from operator import attrgetter
5
8
from django.core.exceptions import ObjectDoesNotExist
6
9
from django.db import connection
7
10
from django.db.models import signals
61
64
# This should never happen. I love comments like this, don't you?
62
65
raise Exception("Impossible arguments to GFK.get_content_type!")
67
def get_prefetch_query_set(self, instances):
68
# For efficiency, group the instances by content type and then do one
70
fk_dict = defaultdict(set)
71
# We need one instance for each group in order to get the right db:
73
ct_attname = self.model._meta.get_field(self.ct_field).get_attname()
74
for instance in instances:
75
# We avoid looking for values if either ct_id or fkey value is None
76
ct_id = getattr(instance, ct_attname)
78
fk_val = getattr(instance, self.fk_field)
79
if fk_val is not None:
80
fk_dict[ct_id].add(fk_val)
81
instance_dict[ct_id] = instance
84
for ct_id, fkeys in fk_dict.items():
85
instance = instance_dict[ct_id]
86
ct = self.get_content_type(id=ct_id, using=instance._state.db)
87
ret_val.extend(ct.get_all_objects_for_this_type(pk__in=fkeys))
64
89
def __get__(self, instance, instance_type=None):
65
90
if instance is None:
226
251
target_col_name = qn(self.field.m2m_reverse_name()),
227
252
content_type = ContentType.objects.db_manager(instance._state.db).get_for_model(instance),
228
253
content_type_field_name = self.field.content_type_field_name,
229
object_id_field_name = self.field.object_id_field_name
254
object_id_field_name = self.field.object_id_field_name,
255
prefetch_cache_name = self.field.attname,
249
275
class GenericRelatedObjectManager(superclass):
250
276
def __init__(self, model=None, core_filters=None, instance=None, symmetrical=None,
251
277
join_table=None, source_col_name=None, target_col_name=None, content_type=None,
252
content_type_field_name=None, object_id_field_name=None):
278
content_type_field_name=None, object_id_field_name=None,
279
prefetch_cache_name=None):
254
281
super(GenericRelatedObjectManager, self).__init__()
255
282
self.core_filters = core_filters or {}
263
290
self.target_col_name = target_col_name
264
291
self.content_type_field_name = content_type_field_name
265
292
self.object_id_field_name = object_id_field_name
293
self.prefetch_cache_name = prefetch_cache_name
266
294
self.pk_val = self.instance._get_pk_val()
268
296
def get_query_set(self):
269
db = self._db or router.db_for_read(self.model, instance=self.instance)
298
return self.instance._prefetched_objects_cache[self.prefetch_cache_name]
299
except (AttributeError, KeyError):
300
db = self._db or router.db_for_read(self.model, instance=self.instance)
302
'%s__pk' % self.content_type_field_name : self.content_type.id,
303
'%s__exact' % self.object_id_field_name : self.pk_val,
305
return superclass.get_query_set(self).using(db).filter(**query)
307
def get_prefetch_query_set(self, instances):
308
db = self._db or router.db_for_read(self.model, instance=instances[0])
271
'%s__pk' % self.content_type_field_name : self.content_type.id,
272
'%s__exact' % self.object_id_field_name : self.pk_val,
274
return superclass.get_query_set(self).using(db).filter(**query)
310
'%s__pk' % self.content_type_field_name: self.content_type.id,
311
'%s__in' % self.object_id_field_name:
312
set(obj._get_pk_val() for obj in instances)
314
qs = super(GenericRelatedObjectManager, self).get_query_set().using(db).filter(**query)
316
attrgetter(self.object_id_field_name),
317
lambda obj: obj._get_pk_val(),
319
self.prefetch_cache_name)
276
321
def add(self, *objs):