From b694c6a387c3afc3373f956eec5c466410fe36a7 Mon Sep 17 00:00:00 2001 From: collerek Date: Mon, 28 Sep 2020 14:30:21 +0200 Subject: [PATCH] change expr to sting in bulk_update, update readme, bump version --- .coverage | Bin 53248 -> 53248 bytes README.md | 46 +++++++++++++++++++++++++++++++++++++ ormar/__init__.py | 2 +- ormar/queryset/queryset.py | 28 +++++++++++----------- 4 files changed, 60 insertions(+), 16 deletions(-) diff --git a/.coverage b/.coverage index 10f7b3abf9fcf1dcd5d37bfab5b6923e3a2571ca..3090f8765b38c0a1a49a5358606a4e836b6b02f7 100644 GIT binary patch delta 139 zcmV;60CfL=paX!Q1F$_W3OFD!G&(XlIx{x2M=!rn0fLhUfFv)k4vY?Y4q*;h4onU^ z4lE8C4hRkV4dD&Q4Zsbu4SWq;4L%J74DJlr49g6i41%)}5GV|@fs8EzEGiNN0SQ(T tx?k-7x3}-x@9tlHZ+~|Gy!W$y)<6H;-M#PMUA_7DZ~yFbXS3UmB|%EcI1>N> delta 140 zcmV;70CWFz{w_?%wzBuHO9nw}1Be4hOT_jwL~aK{%iQ diff --git a/README.md b/README.md index ab8f88f..5c5701a 100644 --- a/README.md +++ b/README.md @@ -328,6 +328,52 @@ assert await Book.objects.count() == 1 ``` + +Since version >=0.3.5 Ormar supports also bulk operations -> bulk_create and bulk_update +```python +import databases +import ormar +import sqlalchemy + +database = databases.Database("sqlite:///db.sqlite") +metadata = sqlalchemy.MetaData() + + +class ToDo(ormar.Model): + class Meta: + tablename = "todos" + metadata = metadata + database = database + + id: ormar.Integer(primary_key=True) + text: ormar.String(max_length=500) + completed: ormar.Boolean(default=False) + +# create multiple instances at once with bulk_create +await ToDo.objects.bulk_create( + [ + ToDo(text="Buy the groceries."), + ToDo(text="Call Mum.", completed=True), + ToDo(text="Send invoices.", completed=True), + ] + ) + +todoes = await ToDo.objects.all() +assert len(todoes) == 3 + +# update objects +for todo in todoes: + todo.completed = False + +# perform update of all objects at once +# objects need to have pk column set, otherwise exception is raised +await ToDo.objects.bulk_update(todoes) + +completed = await ToDo.objects.filter(completed=False).all() +assert len(completed) == 3 + +``` + ## Data types The following keyword arguments are supported on all field types. diff --git a/ormar/__init__.py b/ormar/__init__.py index 0ee9069..079c5b8 100644 --- a/ormar/__init__.py +++ b/ormar/__init__.py @@ -26,7 +26,7 @@ class UndefinedType: # pragma no cover Undefined = UndefinedType() -__version__ = "0.3.4" +__version__ = "0.3.5" __all__ = [ "Integer", "BigInteger", diff --git a/ormar/queryset/queryset.py b/ormar/queryset/queryset.py index 81684d7..c3c1b7d 100644 --- a/ormar/queryset/queryset.py +++ b/ormar/queryset/queryset.py @@ -2,6 +2,7 @@ from typing import Any, List, Mapping, TYPE_CHECKING, Tuple, Type, Union import databases import sqlalchemy +from sqlalchemy import bindparam import ormar # noqa I100 from ormar import MultipleMatches, NoMatch @@ -263,7 +264,7 @@ class QuerySet: async def bulk_update( self, objects: List["Model"], columns: List[str] = None ) -> None: - ready_expressions = [] + ready_objects = [] pk_name = self.model_cls.Meta.pkname if not columns: columns = self.model_cls.extract_db_own_fields().union( @@ -281,18 +282,15 @@ class QuerySet: f"{self.model_cls.__name__} has to have {pk_name} filled." ) new_kwargs = self.model_cls.substitute_models_with_pks(new_kwargs) - new_kwargs = self._populate_default_values(new_kwargs) - new_kwargs = {k: v for k, v in new_kwargs.items() if k in columns} - expr = self.table.update().values( - **{k: v for k, v in new_kwargs.items() if k != pk_name} - ) - pk_column = self.model_cls.Meta.table.c.get(pk_name) - expr = expr.where(pk_column == new_kwargs.get(pk_name)) - ready_expressions.append(expr) + new_kwargs = {"new_" + k: v for k, v in new_kwargs.items() if k in columns} + ready_objects.append(new_kwargs) - # databases does not bind params for where clause and values separately - # no way to pass one dict with both uses - # so we need to resort to lower connection api - async with self.model_cls.Meta.database.connection() as connection: - for single_query in ready_expressions: - await connection.execute(single_query) + pk_column = self.model_cls.Meta.table.c.get(pk_name) + expr = self.table.update().where(pk_column == bindparam("new_" + pk_name)) + expr = expr.values( + **{k: bindparam("new_" + k) for k in columns if k != pk_name} + ) + # databases bind params only where query is passed as string + # otherwise it just pases all data to values and results in unconsumed columns + expr = str(expr) + await self.database.execute_many(expr, ready_objects)