Example #1
0
    async def delete(self, each: bool = False, **kwargs: Any) -> int:
        """
        Deletes from the model table after applying the filters from kwargs.

        You have to either pass a filter to narrow down a query or explicitly pass
        each=True flag to affect whole table.

        :param each: flag if whole table should be affected if no filter is passed
        :type each: bool
        :param kwargs: fields names and proper value types
        :type kwargs: Any
        :return: number of deleted rows
        :rtype:int
        """
        if kwargs:
            return await self.filter(**kwargs).delete()
        if not each and not (self.filter_clauses or self.exclude_clauses):
            raise QueryDefinitionError(
                "You cannot delete without filtering the queryset first. "
                "If you want to delete all rows use delete(each=True)")
        expr = FilterQuery(filter_clauses=self.filter_clauses).apply(
            self.table.delete())
        expr = FilterQuery(filter_clauses=self.exclude_clauses,
                           exclude=True).apply(expr)
        return await self.database.execute(expr)
Example #2
0
    async def update(self, each: bool = False, **kwargs: Any) -> int:
        """
        Updates the model table after applying the filters from kwargs.

        You have to either pass a filter to narrow down a query or explicitly pass
        each=True flag to affect whole table.

        :param each: flag if whole table should be affected if no filter is passed
        :type each: bool
        :param kwargs: fields names and proper value types
        :type kwargs: Any
        :return: number of updated rows
        :rtype: int
        """
        if not each and not (self.filter_clauses or self.exclude_clauses):
            raise QueryDefinitionError(
                "You cannot update without filtering the queryset first. "
                "If you want to update all rows use update(each=True, **kwargs)"
            )

        self_fields = self.model.extract_db_own_fields().union(
            self.model.extract_related_names())
        updates = {k: v for k, v in kwargs.items() if k in self_fields}
        updates = self.model.validate_choices(updates)
        updates = self.model.translate_columns_to_aliases(updates)

        expr = FilterQuery(filter_clauses=self.filter_clauses).apply(
            self.table.update().values(**updates))
        expr = FilterQuery(filter_clauses=self.exclude_clauses,
                           exclude=True).apply(expr)
        return await self.database.execute(expr)
Example #3
0
File: query.py Project: dudil/ormar
    def _apply_expression_modifiers(
        self, expr: sqlalchemy.sql.select
    ) -> sqlalchemy.sql.select:
        """
        Receives the select query (might be join) and applies:
        * Filter clauses
        * Exclude filter clauses
        * Limit clauses
        * Offset clauses
        * Order by clauses

        Returns complete ready to run query.

        :param expr: select expression before clauses
        :type expr: sqlalchemy.sql.selectable.Select
        :return: expresion with all present clauses applied
        :rtype: sqlalchemy.sql.selectable.Select
        """
        expr = FilterQuery(filter_clauses=self.filter_clauses).apply(expr)
        expr = FilterQuery(filter_clauses=self.exclude_clauses, exclude=True).apply(
            expr
        )
        if not self._pagination_query_required():
            expr = LimitQuery(limit_count=self.limit_count).apply(expr)
            expr = OffsetQuery(query_offset=self.query_offset).apply(expr)
        expr = OrderQuery(sorted_orders=self.sorted_orders).apply(expr)
        return expr
Example #4
0
 def _apply_expression_modifiers(
         self, expr: sqlalchemy.sql.select) -> sqlalchemy.sql.select:
     expr = FilterQuery(filter_clauses=self.filter_clauses).apply(expr)
     expr = FilterQuery(filter_clauses=self.exclude_clauses,
                        exclude=True).apply(expr)
     expr = LimitQuery(limit_count=self.limit_count).apply(expr)
     expr = OffsetQuery(query_offset=self.query_offset).apply(expr)
     expr = OrderQuery(sorted_orders=self.sorted_orders).apply(expr)
     return expr
Example #5
0
 async def delete(self, each: bool = False, **kwargs: Any) -> int:
     if kwargs:
         return await self.filter(**kwargs).delete()
     if not each and not self.filter_clauses:
         raise QueryDefinitionError(
             "You cannot delete without filtering the queryset first. "
             "If you want to delete all rows use delete(each=True)")
     expr = FilterQuery(filter_clauses=self.filter_clauses).apply(
         self.table.delete())
     return await self.database.execute(expr)
Example #6
0
 async def update(self, each: bool = False, **kwargs: Any) -> int:
     self_fields = self.model.extract_db_own_fields()
     updates = {k: v for k, v in kwargs.items() if k in self_fields}
     updates = self.model.translate_columns_to_aliases(updates)
     if not each and not self.filter_clauses:
         raise QueryDefinitionError(
             "You cannot update without filtering the queryset first. "
             "If you want to update all rows use update(each=True, **kwargs)"
         )
     expr = FilterQuery(filter_clauses=self.filter_clauses).apply(
         self.table.update().values(**updates))
     return await self.database.execute(expr)
Example #7
0
    def _build_pagination_condition(
        self,
    ) -> Tuple[sqlalchemy.sql.expression.TextClause,
               sqlalchemy.sql.expression.TextClause]:
        """
        In order to apply limit and offset on main table in join only
        (otherwise you can get only partially constructed main model
        if number of children exceeds the applied limit and select_related is used)

        Used also to change first and get() without argument behaviour.
        Needed only if limit or offset are set, the flag limit_sql_raw is not set
        and query has select_related applied. Otherwise we can limit/offset normally
        at the end of whole query.

        The condition is added to filters to filter out desired number of main model
        primary key values. Whole query is used to determine the values.
        """
        pk_alias = self.model_cls.get_column_alias(self.model_cls.Meta.pkname)
        pk_aliased_name = f"{self.table.name}.{pk_alias}"
        qry_text = sqlalchemy.text(f"{pk_aliased_name}")
        maxes = OrderedDict()
        for order in list(self.sorted_orders.keys()):
            if order is not None and order.get_field_name_text(
            ) != pk_aliased_name:
                aliased_col = order.get_field_name_text()
                maxes[aliased_col] = order.get_min_or_max()
            elif order.get_field_name_text() == pk_aliased_name:
                maxes[pk_aliased_name] = order.get_text_clause()

        limit_qry = sqlalchemy.sql.select([qry_text])
        limit_qry = limit_qry.select_from(self.select_from)
        limit_qry = FilterQuery(
            filter_clauses=self.filter_clauses).apply(limit_qry)
        limit_qry = FilterQuery(filter_clauses=self.exclude_clauses,
                                exclude=True).apply(limit_qry)
        limit_qry = limit_qry.group_by(qry_text)
        for order_by in maxes.values():
            limit_qry = limit_qry.order_by(order_by)
        limit_qry = LimitQuery(limit_count=self.limit_count).apply(limit_qry)
        limit_qry = OffsetQuery(
            query_offset=self.query_offset).apply(limit_qry)
        limit_qry = limit_qry.alias("limit_query")
        on_clause = sqlalchemy.text(
            f"limit_query.{pk_alias}={self.table.name}.{pk_alias}")
        return limit_qry, on_clause
Example #8
0
    def _build_pagination_subquery(self) -> sqlalchemy.sql.select:
        """
        In order to apply limit and offset on main table in join only
        (otherwise you can get only partially constructed main model
        if number of children exceeds the applied limit and select_related is used)

        Used also to change first and get() without argument behaviour.
        Needed only if limit or offset are set, the flag limit_sql_raw is not set
        and query has select_related applied. Otherwise we can limit/offset normally
        at the end of whole query.

        :return: constructed subquery on main table with limit, offset and order applied
        :rtype: sqlalchemy.sql.select
        """
        expr = sqlalchemy.sql.select(self.model_cls.Meta.table.columns)
        expr = LimitQuery(limit_count=self.limit_count).apply(expr)
        expr = OffsetQuery(query_offset=self.query_offset).apply(expr)
        filters_to_use = [
            filter_clause for filter_clause in self.filter_clauses
            if filter_clause.table_prefix == ""
        ]
        excludes_to_use = [
            filter_clause for filter_clause in self.exclude_clauses
            if filter_clause.table_prefix == ""
        ]
        sorts_to_use = {
            k: v
            for k, v in self.sorted_orders.items() if "__" not in k
        }
        expr = FilterQuery(filter_clauses=filters_to_use).apply(expr)
        expr = FilterQuery(filter_clauses=excludes_to_use,
                           exclude=True).apply(expr)
        expr = OrderQuery(sorted_orders=sorts_to_use).apply(expr)
        expr = expr.alias(f"{self.table}")
        self.filter_clauses = list(
            set(self.filter_clauses) - set(filters_to_use))
        self.exclude_clauses = list(
            set(self.exclude_clauses) - set(excludes_to_use))
        return expr