Beispiel #1
0
    def _escape_characters_in_clause(self) -> None:
        """
        Escapes the special characters ["%", "_"] if needed.
        Adds `%` for `like` queries.

        :raises QueryDefinitionError: if contains or icontains is used with
        ormar model instance
        :return: escaped value and flag if escaping is needed
        :rtype: Tuple[Any, bool]
        """
        self.has_escaped_character = False
        if self.operator in [
                "contains",
                "icontains",
                "startswith",
                "istartswith",
                "endswith",
                "iendswith",
        ]:
            if isinstance(self.filter_value, ormar.Model):
                raise QueryDefinitionError(
                    "You cannot use contains and icontains with instance of the Model"
                )
            self.has_escaped_character = self.has_escaped_characters()
            if self.has_escaped_character:
                self._escape_chars()
            self._prefix_suffix_quote()
Beispiel #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
        """
        # queryset proxy always have one filter for pk of parent model
        if not each and len(self.queryset.filter_clauses) == 1:
            raise QueryDefinitionError(
                "You cannot update without filtering the queryset first. "
                "If you want to update all rows use update(each=True, **kwargs)"
            )

        through_kwargs = kwargs.pop(self.through_model_name, {})
        children = await self.queryset.all()
        for child in children:
            await child.update(**kwargs)  # type: ignore
            if self.type_ == ormar.RelationType.MULTIPLE and through_kwargs:
                await self.update_through_instance(
                    child=child,  # type: ignore
                    **through_kwargs,
                )
        return len(children)
Beispiel #3
0
    async def delete(self,
                     *args: Any,
                     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 or args:
            return await self.filter(*args, **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)
Beispiel #4
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)
Beispiel #5
0
    def _resolve_filter_groups(
            self, groups: Any) -> Tuple[List[FilterGroup], List[str]]:
        """
        Resolves filter groups to populate FilterAction params in group tree.

        :param groups: tuple of FilterGroups
        :type groups: Any
        :return: list of resolver groups
        :rtype: Tuple[List[FilterGroup], List[str]]
        """
        filter_groups = []
        select_related = self._select_related
        if groups:
            for group in groups:
                if not isinstance(group, FilterGroup):
                    raise QueryDefinitionError(
                        "Only ormar.and_ and ormar.or_ "
                        "can be passed as filter positional"
                        " arguments,"
                        "other values need to be passed by"
                        "keyword arguments")
                _, select_related = group.resolve(
                    model_cls=self.model,
                    select_related=self._select_related,
                    filter_clauses=self.filter_clauses,
                )
                filter_groups.append(group)
        return filter_groups, select_related
Beispiel #6
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)
Beispiel #7
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)
Beispiel #8
0
 async def _query_aggr_function(self, func_name: str, columns: List) -> Any:
     func = getattr(sqlalchemy.func, func_name)
     select_actions = [
         SelectAction(select_str=column, model_cls=self.model) for column in columns
     ]
     if func_name in ["sum", "avg"]:
         if any(not x.is_numeric for x in select_actions):
             raise QueryDefinitionError(
                 "You can use sum and svg only with" "numeric types of columns"
             )
     select_columns = [x.apply_func(func, use_label=True) for x in select_actions]
     expr = self.build_select_expression().alias(f"subquery_for_{func_name}")
     expr = sqlalchemy.select(select_columns).select_from(expr)
     # print("\n", expr.compile(compile_kwargs={"literal_binds": True}))
     result = await self.database.fetch_one(expr)
     return dict(result) if len(result) > 1 else result[0]  # type: ignore
Beispiel #9
0
    async def bulk_update(  # noqa:  CCR001
            self,
            objects: List["Model"],
            columns: List[str] = None) -> None:
        ready_objects = []
        pk_name = self.model_meta.pkname
        if not columns:
            columns = list(self.model.extract_db_own_fields().union(
                self.model.extract_related_names()))

        if pk_name not in columns:
            columns.append(pk_name)

        columns = [self.model.get_column_alias(k) for k in columns]

        for objt in objects:
            new_kwargs = objt.dict()
            if pk_name not in new_kwargs or new_kwargs.get(pk_name) is None:
                raise QueryDefinitionError(
                    "You cannot update unsaved objects. "
                    f"{self.model.__name__} has to have {pk_name} filled.")
            new_kwargs = self.model.substitute_models_with_pks(new_kwargs)
            new_kwargs = self.model.translate_columns_to_aliases(new_kwargs)
            new_kwargs = {
                "new_" + k: v
                for k, v in new_kwargs.items() if k in columns
            }
            ready_objects.append(new_kwargs)

        pk_column = self.model_meta.table.c.get(
            self.model.get_column_alias(pk_name))
        pk_column_name = self.model.get_column_alias(pk_name)
        table_columns = [c.name for c in self.model_meta.table.c]
        expr = self.table.update().where(
            pk_column == bindparam("new_" + pk_column_name))
        expr = expr.values(
            **{
                k: bindparam("new_" + k)
                for k in columns if k != pk_column_name and k in table_columns
            })
        # databases bind params only where query is passed as string
        # otherwise it just passes all data to values and results in unconsumed columns
        expr = str(expr)
        await self.database.execute_many(expr, ready_objects)

        for objt in objects:
            objt.set_save_status(True)
Beispiel #10
0
    def paginate(self, page: int, page_size: int = 20) -> "QuerySet[T]":
        """
        You can paginate the result which is a combination of offset and limit clauses.
        Limit is set to page size and offset is set to (page-1) * page_size.

        :param page_size: numbers of items per page
        :type page_size: int
        :param page: page number
        :type page: int
        :return: QuerySet
        :rtype: QuerySet
        """
        if page < 1 or page_size < 1:
            raise QueryDefinitionError("Page size and page have to be greater than 0.")

        limit_count = page_size
        query_offset = (page - 1) * page_size
        return self.rebuild_self(limit_count=limit_count, offset=query_offset,)