Beispiel #1
0
    async def create_through_instance(self, child: "Model",
                                      **kwargs: Any) -> None:
        """
        Crete a through model instance in the database for m2m relations.

        :param kwargs: dict of additional keyword arguments for through instance
        :type kwargs: Any
        :param child: child model instance
        :type child: Model
        """
        model_cls = self.relation.through
        owner_column = self.related_field.default_target_field_name(
        )  # type: ignore
        child_column = self.related_field.default_source_field_name(
        )  # type: ignore
        rel_kwargs = {owner_column: self._owner.pk, child_column: child.pk}
        final_kwargs = {**rel_kwargs, **kwargs}
        if child.pk is None:
            raise ModelPersistenceError(f"You cannot save {child.get_name()} "
                                        f"model without primary key set! \n"
                                        f"Save the child model first.")
        expr = model_cls.Meta.table.insert()
        expr = expr.values(**final_kwargs)
        # print("\n", expr.compile(compile_kwargs={"literal_binds": True}))
        await model_cls.Meta.database.execute(expr)
Beispiel #2
0
    def _extract_model_db_fields(self) -> Dict:
        """
        Returns a dictionary with field names and values for fields that are stored in
        current model's table.

        That includes own non-relational fields ang foreign key fields.

        :return: dictionary of fields names and values.
        :rtype: Dict
        """
        self_fields = self._extract_own_model_fields()
        self_fields = {
            k: v
            for k, v in self_fields.items()
            if self.get_column_alias(k) in self.Meta.table.columns
        }
        for field in self._extract_db_related_names():
            relation_field = self.Meta.model_fields[field]
            target_pk_name = relation_field.to.Meta.pkname
            target_field = getattr(self, field)
            self_fields[field] = getattr(target_field, target_pk_name, None)
            if not relation_field.nullable and not self_fields[field]:
                raise ModelPersistenceError(
                    f"You cannot save {relation_field.to.get_name()} "
                    f"model without pk set!")
        return self_fields
Beispiel #3
0
    def substitute_models_with_pks(cls,
                                   model_dict: Dict) -> Dict:  # noqa  CCR001
        """
        Receives dictionary of model that is about to be saved and changes all related
        models that are stored as foreign keys to their fk value.

        :param model_dict: dictionary of model that is about to be saved
        :type model_dict: Dict
        :return: dictionary of model that is about to be saved
        :rtype: Dict
        """
        for field in cls.extract_related_names():
            field_value = model_dict.get(field, None)
            if field_value is not None:
                target_field = cls.Meta.model_fields[field]
                target_pkname = target_field.to.Meta.pkname
                if isinstance(field_value, ormar.Model):
                    pk_value = getattr(field_value, target_pkname)
                    if not pk_value:
                        raise ModelPersistenceError(
                            f"You cannot save {field_value.get_name()} "
                            f"model without pk set!")
                    model_dict[field] = pk_value
                elif field_value:  # nested dict
                    if isinstance(field_value, list):
                        model_dict[field] = [
                            target.get(target_pkname) for target in field_value
                        ]
                    else:
                        model_dict[field] = field_value.get(target_pkname)
                else:
                    model_dict.pop(field, None)
        return model_dict
Beispiel #4
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 ModelPersistenceError(
                    "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 #5
0
    async def update(self: T, _columns: List[str] = None, **kwargs: Any) -> T:
        """
        Performs update of Model instance in the database.
        Fields can be updated before or you can pass them as kwargs.

        Sends pre_update and post_update signals.

        Sets model save status to True.

        :param _columns: list of columns to update, if None all are updated
        :type _columns: List
        :raises ModelPersistenceError: If the pk column is not set

        :param kwargs: list of fields to update as field=value pairs
        :type kwargs: Any
        :return: updated Model
        :rtype: Model
        """
        if kwargs:
            self.update_from_dict(kwargs)

        if not self.pk:
            raise ModelPersistenceError(
                "You cannot update not saved model! Use save or upsert method."
            )

        await self.signals.pre_update.send(sender=self.__class__,
                                           instance=self,
                                           passed_args=kwargs)
        self_fields = self._extract_model_db_fields()
        self_fields.pop(self.get_column_name_from_alias(self.Meta.pkname))
        if _columns:
            self_fields = {
                k: v
                for k, v in self_fields.items() if k in _columns
            }
        self_fields = self.translate_columns_to_aliases(self_fields)
        expr = self.Meta.table.update().values(**self_fields)
        expr = expr.where(self.pk_column == getattr(self, self.Meta.pkname))

        await self.Meta.database.execute(expr)
        self.set_save_status(True)
        await self.signals.post_update.send(sender=self.__class__,
                                            instance=self)
        return self
Beispiel #6
0
    async def update(self: T, **kwargs: Any) -> T:
        if kwargs:
            new_values = {**self.dict(), **kwargs}
            self.from_dict(new_values)

        if not self.pk:
            raise ModelPersistenceError(
                "You cannot update not saved model! Use save or upsert method."
            )

        self_fields = self._extract_model_db_fields()
        self_fields.pop(self.get_column_name_from_alias(self.Meta.pkname))
        self_fields = self.translate_columns_to_aliases(self_fields)
        expr = self.Meta.table.update().values(**self_fields)
        expr = expr.where(self.pk_column == getattr(self, self.Meta.pkname))

        await self.Meta.database.execute(expr)
        self.set_save_status(True)
        return self
Beispiel #7
0
    async def create_through_instance(self, child: "T", **kwargs: Any) -> None:
        """
        Crete a through model instance in the database for m2m relations.

        :param kwargs: dict of additional keyword arguments for through instance
        :type kwargs: Any
        :param child: child model instance
        :type child: Model
        """
        model_cls = self.relation.through
        owner_column = self.related_field.default_target_field_name(
        )  # type: ignore
        child_column = self.related_field.default_source_field_name(
        )  # type: ignore
        rel_kwargs = {owner_column: self._owner.pk, child_column: child.pk}
        final_kwargs = {**rel_kwargs, **kwargs}
        if child.pk is None:
            raise ModelPersistenceError(f"You cannot save {child.get_name()} "
                                        f"model without primary key set! \n"
                                        f"Save the child model first.")
        await model_cls(**final_kwargs).save()
Beispiel #8
0
 def substitute_models_with_pks(cls,
                                model_dict: Dict) -> Dict:  # noqa  CCR001
     for field in cls.extract_related_names():
         field_value = model_dict.get(field, None)
         if field_value is not None:
             target_field = cls.Meta.model_fields[field]
             target_pkname = target_field.to.Meta.pkname
             if isinstance(field_value, ormar.Model):
                 pk_value = getattr(field_value, target_pkname)
                 if not pk_value:
                     raise ModelPersistenceError(
                         f"You cannot save {field_value.get_name()} "
                         f"model without pk set!")
                 model_dict[field] = pk_value
             elif field_value:  # nested dict
                 if isinstance(field_value, list):
                     model_dict[field] = [
                         target.get(target_pkname) for target in field_value
                     ]
                 else:
                     model_dict[field] = field_value.get(target_pkname)
             else:
                 model_dict.pop(field, None)
     return model_dict
Beispiel #9
0
    async def bulk_update(  # noqa:  CCR001
            self,
            objects: List["Model"],
            columns: List[str] = None) -> None:
        """
        Performs bulk update in one database session to speed up the process.

        Allows to update multiple instance at once.

        All `Models` passed need to have primary key column populated.

        You can also select which fields to update by passing `columns` list
        as a list of string names.

        Bulk operations do not send signals.

        :param objects: list of ormar models
        :type objects: List[Model]
        :param columns: list of columns to update
        :type columns: List[str]
        """
        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 ModelPersistenceError(
                    "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)