Example #1
0
    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]
            )
Example #2
0
    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
Example #3
0
    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
Example #4
0
    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
Example #5
0
 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
Example #6
0
    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
Example #7
0
    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
Example #8
0
    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
Example #9
0
 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
Example #10
0
    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
Example #11
0
    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