def resolve_filters(self, model, q_objects, annotations, custom_filters) -> None: modifier = QueryModifier() for node in q_objects: modifier &= node.resolve(model, annotations, custom_filters) where_criterion, joins, having_criterion = modifier.get_query_modifiers( ) for join in joins: if join[0] not in self._joined_tables: self.query = self.query.join( join[0], how=JoinType.left_outer).on(join[1]) self._joined_tables.append(join[0]) if where_criterion: self.query = self.query.where(where_criterion) if having_criterion: self.query = self.query.having(having_criterion)
def resolve_filters( self, model: "Type[Model]", q_objects: List[Q], annotations: Dict[str, Any], custom_filters: Dict[str, Dict[str, Any]], ) -> None: """ Builds the common filters for a QuerySet. :param model: The Model this queryset is based on. :param q_objects: The Q expressions to apply. :param annotations: Extra annotations to add. :param custom_filters: Pre-resolved filters to be passed through. """ has_aggregate = self._resolve_annotate() modifier = QueryModifier() for node in q_objects: modifier &= node.resolve(model, annotations, custom_filters, model._meta.basetable) where_criterion, joins, having_criterion = modifier.get_query_modifiers( ) for join in joins: if join[0] not in self._joined_tables: self.query = self.query.join( join[0], how=JoinType.left_outer).on(join[1]) self._joined_tables.append(join[0]) self.query._wheres = where_criterion self.query._havings = having_criterion if has_aggregate and (self._joined_tables or having_criterion or self.query._orderbys): self.query = self.query.groupby( self.model._meta.basetable[self.model._meta.db_pk_column])
async def _prefetch_m2m_relation(self, instance_list: list, field: str, related_query) -> list: instance_id_set = {instance.pk for instance in instance_list} # type: Set[Any] field_object = self.model._meta.fields_map[field] through_table = Table(field_object.through) subquery = ( self.db.query_class.from_(through_table) .select( getattr(through_table, field_object.backward_key).as_("_backward_relation_key"), getattr(through_table, field_object.forward_key).as_("_forward_relation_key"), ) .where(getattr(through_table, field_object.backward_key).isin(instance_id_set)) ) related_query_table = Table(related_query.model._meta.table) related_pk_field = related_query.model._meta.db_pk_field query = ( related_query.query.join(subquery) .on(subquery._forward_relation_key == getattr(related_query_table, related_pk_field)) .select( subquery._backward_relation_key.as_("_backward_relation_key"), *[getattr(related_query_table, field).as_(field) for field in related_query.fields], ) ) if related_query._q_objects: joined_tables = [] # type: List[Table] modifier = QueryModifier() for node in related_query._q_objects: modifier &= node.resolve( model=related_query.model, annotations=related_query._annotations, custom_filters=related_query._custom_filters, ) where_criterion, joins, having_criterion = modifier.get_query_modifiers() for join in joins: if join[0] not in joined_tables: query = query.join(join[0], how=JoinType.left_outer).on(join[1]) joined_tables.append(join[0]) if where_criterion: query = query.where(where_criterion) if having_criterion: query = query.having(having_criterion) raw_results = await self.db.execute_query(query.get_sql()) relations = { ( self.model._meta.pk.to_python_value(e["_backward_relation_key"]), field_object.type._meta.pk.to_python_value(e[related_pk_field]), ) for e in raw_results } related_object_list = [related_query.model(_from_db=True, **e) for e in raw_results] await self.__class__( model=related_query.model, db=self.db, prefetch_map=related_query._prefetch_map ).fetch_for_list(related_object_list) related_object_map = {e.pk: e for e in related_object_list} relation_map = {} # type: Dict[str, list] for object_id, related_object_id in relations: if object_id not in relation_map: relation_map[object_id] = [] relation_map[object_id].append(related_object_map[related_object_id]) for instance in instance_list: relation_container = getattr(instance, field) relation_container._set_result_for_query(relation_map.get(instance.pk, [])) return instance_list
async def _prefetch_m2m_relation( self, instance_list: "Iterable[Model]", field: str, related_query: Tuple[Optional[str], "QuerySet"], ) -> "Iterable[Model]": to_attr, related_query = related_query instance_id_set: set = { self._field_to_db(instance._meta.pk, instance.pk, instance) for instance in instance_list } field_object: ManyToManyFieldInstance = self.model._meta.fields_map[ field] # type: ignore through_table = Table(field_object.through) subquery = (self.db.query_class.from_(through_table).select( through_table[field_object.backward_key].as_( "_backward_relation_key"), through_table[field_object.forward_key].as_( "_forward_relation_key"), ).where( through_table[field_object.backward_key].isin(instance_id_set))) related_query_table = related_query.model._meta.basetable related_pk_field = related_query.model._meta.db_pk_column related_query.resolve_ordering(related_query.model, related_query_table, [], {}) query = (related_query.query.join(subquery).on( subquery._forward_relation_key == related_query_table[related_pk_field]).select( subquery._backward_relation_key.as_("_backward_relation_key"), *[ related_query_table[field].as_(field) for field in related_query.fields ], )) if related_query._q_objects: joined_tables: List[Table] = [] modifier = QueryModifier() for node in related_query._q_objects: modifier &= node.resolve( model=related_query.model, annotations=related_query._annotations, custom_filters=related_query._custom_filters, table=related_query_table, ) where_criterion, joins, having_criterion = modifier.get_query_modifiers( ) for join in joins: if join[0] not in joined_tables: query = query.join(join[0], how=JoinType.left_outer).on(join[1]) joined_tables.append(join[0]) if where_criterion: query = query.where(where_criterion) if having_criterion: query = query.having(having_criterion) _, raw_results = await self.db.execute_query(query.get_sql()) # TODO: we should only resolve the PK's once relations = [( self.model._meta.pk.to_python_value(e["_backward_relation_key"]), field_object.related_model._meta.pk.to_python_value( e[related_pk_field]), ) for e in raw_results] related_object_list = [ related_query.model._init_from_db(**e) for e in raw_results ] await self.__class__(model=related_query.model, db=self.db, prefetch_map=related_query._prefetch_map )._execute_prefetch_queries(related_object_list) related_object_map = {e.pk: e for e in related_object_list} relation_map: Dict[str, list] = {} for object_id, related_object_id in relations: if object_id not in relation_map: relation_map[object_id] = [] relation_map[object_id].append( related_object_map[related_object_id]) for instance in instance_list: relation_container = getattr(instance, field) relation_container._set_result_for_query( relation_map.get(instance.pk, []), to_attr) return instance_list