Exemplo n.º 1
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])

        if where_criterion:
            self.query = self.query.where(where_criterion)

        if having_criterion:
            self.query = self.query.having(having_criterion)
Exemplo n.º 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 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])
Exemplo n.º 3
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
Exemplo n.º 4
0
    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