def get_related_objects(instances, filters=None): queryset = None apply_filters = mk_filter_function(filters) # functioning defining a relationship if type(rel_obj_descriptor) in (types.FunctionType, types.MethodType): return rel_obj_descriptor(instances, apply_filters) # FK from instance to a related object elif type(rel_obj_descriptor) == ReverseSingleRelatedObjectDescriptor: field = rel_obj_descriptor.field rel_obj_attr = field.get_foreign_related_value instance_attr = field.get_local_related_value query = {'%s__in' % field.related_query_name(): instances} rel_mgr = field.rel.to._default_manager # If the related manager indicates that it should be used for related # fields, respect that. if getattr(rel_mgr, 'use_for_related_fields', False): queryset = rel_mgr else: queryset = QuerySet(field.rel.to) queryset = queryset.filter(**query).only('pk') table = instances[0]._meta.db_table pk_field = instances[0]._meta.pk.column related_table = field.related_field.model._meta.db_table if table == related_table: # XXX hack: assuming django uses T2 for joining two tables of same name table = 'T2' queryset = queryset.extra( select={INPUT_ATTR_PREFIX: '%s.%s' % (table, pk_field)}) # reverse FK from instance to related objects with FK to the instance elif type(rel_obj_descriptor) == ForeignRelatedObjectsDescriptor: rel_field = rel_obj_descriptor.related.field rel_obj_attr = rel_field.get_local_related_value rel_column = rel_field.column rel_model = rel_obj_descriptor.related.model rel_mgr = rel_model._default_manager.__class__() rel_mgr.model = rel_model query = {'%s__in' % rel_field.name: instances} queryset = rel_mgr.get_queryset().filter(**query).only('pk') queryset = queryset.extra( select={INPUT_ATTR_PREFIX: '%s' % rel_column}) # M2M from instance to related objects elif type(rel_obj_descriptor) in (ReverseManyRelatedObjectsDescriptor, ManyRelatedObjectsDescriptor): db = router.db_for_read(instance.__class__, instance=instance) connection = connections[db] mgr = rel_obj_descriptor.__get__(instance) query = {'%s__in' % mgr.query_field_name: instances} queryset = super(mgr.__class__, mgr).get_queryset().filter(**query).only('pk') fk = mgr.through._meta.get_field(mgr.source_field_name) join_table = mgr.through._meta.db_table qn = connection.ops.quote_name queryset = queryset.extra(select=dict( ('%s%s' % (INPUT_ATTR_PREFIX, f.attname), '%s.%s' % (qn(join_table), qn(f.column))) for f in fk.local_related_fields)) # if you just do 'if queryset', that triggers query execution because # python checks length of the enumerable. to prevent query execution, check # if queryset is not None. if queryset is not None: return apply_filters(queryset) return []
def get_related_objects(instances, filters=None): queryset = None apply_filters = mk_filter_function(filters) # functioning defining a relationship if type(rel_obj_descriptor) in (types.FunctionType, types.MethodType): return rel_obj_descriptor(instances, apply_filters) # FK from instance to a related object elif type(rel_obj_descriptor) in (ForwardManyToOneDescriptor, ForwardOneToOneDescriptor): field = rel_obj_descriptor.field rel_obj_attr = field.get_foreign_related_value instance_attr = field.get_local_related_value query = {'%s__in' % field.related_query_name(): instances} rel_mgr = field.rel.to._default_manager # If the related manager indicates that it should be used for related # fields, respect that. if getattr(rel_mgr, 'use_for_related_fields', False): queryset = rel_mgr else: queryset = QuerySet(field.rel.to) queryset = queryset.filter(**query).only('pk') table = instances[0]._meta.db_table pk_field = instances[0]._meta.pk.column related_table = field.rel.model._meta.db_table if table == related_table: # XXX hack: assuming django uses T2 for joining two tables of same name table = 'T2' queryset = queryset.extra(select={INPUT_ATTR_PREFIX: '%s.%s' % (table, pk_field)}) # reverse FK from instance to related objects with FK to the instance elif type(rel_obj_descriptor) in (ReverseManyToOneDescriptor, ReverseOneToOneDescriptor): rel_obj = rel_obj_descriptor.related if (type(rel_obj_descriptor) == ReverseOneToOneDescriptor) else \ rel_obj_descriptor.rel rel_field = rel_obj.field rel_obj_attr = rel_field.get_local_related_value rel_column = rel_field.column rel_model = rel_obj.related_model rel_mgr = rel_model._default_manager.__class__() rel_mgr.model = rel_model query = {'%s__in' % rel_field.name: instances} queryset = rel_mgr.get_queryset().filter(**query).only('pk') queryset = queryset.extra(select={INPUT_ATTR_PREFIX: '%s' % rel_column}) # M2M from instance to related objects elif type(rel_obj_descriptor) in (ReverseManyToOneDescriptor, ManyToManyDescriptor): db = router.db_for_read(instance.__class__, instance=instance) connection = connections[db] mgr = rel_obj_descriptor.__get__(instance) query = {'%s__in' % mgr.query_field_name: instances} queryset = super(mgr.__class__, mgr).get_queryset().filter(**query).only('pk') fk = mgr.through._meta.get_field(mgr.source_field_name) join_table = mgr.through._meta.db_table qn = connection.ops.quote_name queryset = queryset.extra(select=dict( ('%s%s' % (INPUT_ATTR_PREFIX, f.attname), '%s.%s' % (qn(join_table), qn(f.column))) for f in fk.local_related_fields)) # if you just do 'if queryset', that triggers query execution because # python checks length of the enumerable. to prevent query execution, check # if queryset is not None. if queryset is not None: return apply_filters(queryset) return []