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] )
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 querysit is based on. :param q_objects: The Q expressions to apply. :param annotations: Extra annotations to add. :param custom_filters: """ 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
def _resolve_field_for_model(self, model: "Type[Model]", table: Table, field: str) -> dict: ret = super()._resolve_field_for_model(model, table, field) if self.filter: modifier = QueryModifier() modifier &= self.filter.resolve(model, {}, {}, model._meta.basetable) where_criterion, joins, having_criterion = modifier.get_query_modifiers() ret["field"] = Case().when(where_criterion, ret["field"]).else_(None) return ret
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]) self.query._wheres = where_criterion self.query._havings = having_criterion
def _resolve_regular_kwarg(self, model: "Type[Model]", key: str, value: Any, table: Table) -> QueryModifier: if key not in model._meta.filters and key.split( "__")[0] in model._meta.fetch_fields: modifier = self._resolve_nested_filter(model, key, value, table) else: criterion, join = self._process_filter_kwarg( model, key, value, table) joins = [join] if join else [] modifier = QueryModifier(where_criterion=criterion, joins=joins) return modifier
def _resolve_custom_kwarg(self, model: "Type[Model]", key: str, value: Any, table: Table) -> QueryModifier: having_info = self._custom_filters[key] annotation = self._annotations[having_info["field"]] if isinstance(annotation, Term): annotation_info = {"field": annotation} else: annotation_info = annotation.resolve(model, table) operator = having_info["operator"] overridden_operator = model._meta.db.executor_class.get_overridden_filter_func( filter_func=operator) if overridden_operator: operator = overridden_operator if annotation_info["field"].is_aggregate: modifier = QueryModifier( having_criterion=operator(annotation_info["field"], value)) else: modifier = QueryModifier( where_criterion=operator(annotation_info["field"], value)) return modifier
def resolve(self, model: "Type[Model]", table: Table) -> tuple: q_objects = self._resolve_q_objects() modifier = QueryModifier() for node in q_objects: modifier &= node.resolve(model, model._meta.basetable) if isinstance(self.then, Function): then = self.then.resolve(model, table)["field"] elif isinstance(self.then, Term): then = F.resolver_arithmetic_expression(model, self.then)[0] else: then = Term.wrap_constant(self.then) return modifier.where_criterion, then
def _resolve_children(self, model: "Type[Model]", table: Table) -> QueryModifier: modifier = QueryModifier() for node in self.children: node._annotations = self._annotations node._custom_filters = self._custom_filters node_modifier = node.resolve(model, table) if self.join_type == self.AND: modifier &= node_modifier else: modifier |= node_modifier if self._is_negated: modifier = ~modifier return modifier
def _resolve_nested_filter(self, model: "Type[Model]", key: str, value: Any, table: Table) -> QueryModifier: related_field_name, __, forwarded_fields = key.partition("__") related_field = cast(RelationalField, model._meta.fields_map[related_field_name]) required_joins = _get_joins_for_related_field(table, related_field, related_field_name) q = Q(**{forwarded_fields: value}) q._annotations = self._annotations q._custom_filters = self._custom_filters modifier = q.resolve( model=related_field.related_model, table=required_joins[-1][0], ) return QueryModifier(joins=required_joins) & modifier
def _resolve_kwargs(self, model: "Type[Model]", table: Table) -> QueryModifier: modifier = QueryModifier() for raw_key, raw_value in self.filters.items(): key, value = self._get_actual_filter_params( model, raw_key, raw_value) if key in self._custom_filters: filter_modifier = self._resolve_custom_kwarg( model, key, value, table) else: filter_modifier = self._resolve_regular_kwarg( model, key, value, table) if self.join_type == self.AND: modifier &= filter_modifier else: modifier |= filter_modifier if self._is_negated: modifier = ~modifier return modifier
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