change expr to sting in bulk_update, update readme, bump version

This commit is contained in:
collerek
2020-09-28 14:30:21 +02:00
parent a5abf2a403
commit b694c6a387
4 changed files with 60 additions and 16 deletions

BIN
.coverage

Binary file not shown.

View File

@ -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 ## Data types
The following keyword arguments are supported on all field types. The following keyword arguments are supported on all field types.

View File

@ -26,7 +26,7 @@ class UndefinedType: # pragma no cover
Undefined = UndefinedType() Undefined = UndefinedType()
__version__ = "0.3.4" __version__ = "0.3.5"
__all__ = [ __all__ = [
"Integer", "Integer",
"BigInteger", "BigInteger",

View File

@ -2,6 +2,7 @@ from typing import Any, List, Mapping, TYPE_CHECKING, Tuple, Type, Union
import databases import databases
import sqlalchemy import sqlalchemy
from sqlalchemy import bindparam
import ormar # noqa I100 import ormar # noqa I100
from ormar import MultipleMatches, NoMatch from ormar import MultipleMatches, NoMatch
@ -263,7 +264,7 @@ class QuerySet:
async def bulk_update( async def bulk_update(
self, objects: List["Model"], columns: List[str] = None self, objects: List["Model"], columns: List[str] = None
) -> None: ) -> None:
ready_expressions = [] ready_objects = []
pk_name = self.model_cls.Meta.pkname pk_name = self.model_cls.Meta.pkname
if not columns: if not columns:
columns = self.model_cls.extract_db_own_fields().union( 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." 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.model_cls.substitute_models_with_pks(new_kwargs)
new_kwargs = self._populate_default_values(new_kwargs) new_kwargs = {"new_" + k: v for k, v in new_kwargs.items() if k in columns}
new_kwargs = {k: v for k, v in new_kwargs.items() if k in columns} ready_objects.append(new_kwargs)
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)
# databases does not bind params for where clause and values separately pk_column = self.model_cls.Meta.table.c.get(pk_name)
# no way to pass one dict with both uses expr = self.table.update().where(pk_column == bindparam("new_" + pk_name))
# so we need to resort to lower connection api expr = expr.values(
async with self.model_cls.Meta.database.connection() as connection: **{k: bindparam("new_" + k) for k in columns if k != pk_name}
for single_query in ready_expressions: )
await connection.execute(single_query) # 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)