python-gino / gino

GINO Is Not ORM - a Python asyncio ORM on SQLAlchemy core.
https://python-gino.org/
Other
2.67k stars 150 forks source link

Add an easy way to do bulk update #784

Closed moyaldror closed 3 years ago

moyaldror commented 3 years ago

I have a list of rows I wish to update. I want to update them in a similar fashion as done in bulk_update_mappings and could not find such options to do it in gino.

I looked around and saw that some of the solutions are doing upsert but it doesn't fit my needs. Another option I thought about was to iterate on the model instance I have in hand, use the update() command on it (creating a chain of updates), and at the of each update() end call apply() but this is not bulk update.

Is there an option to do such bulk update now which I just can't find? Is there a simple workaround?

wwwjfy commented 3 years ago

It can be done via raw SQL statements.

moyaldror commented 3 years ago

Well, I preferred not to go in this direction. I ended up using something like this:

    async def bulk_update(values):
        update_statement = Entity.update.where(
            Entity.id == sa.bindparam("id")
        ).values(
            {column: sa.bindparam(column) for column in COLUMNS_TO_UPDATE}
        )

        return await db.status(update_statement, values)
wwwjfy commented 3 years ago

Right. I missed the most obvious. It's actually the preferred way to use Gino, executing SQLAlchemy queries. The "ORM" is just for convenience.

Closing it.

nguyenthanhquang152 commented 3 years ago

I have my own example to share with you all:

    update_values = [
        dict(id=1, total_likes=1, live_ref_id='hash_1'),
        dict(id=2, total_likes=0, live_ref_id='hash_2')
    ]
    update_statement = Post.update.where(Post.id == sqlalchemy.bindparam('id')) \
        .values({
            'id': sqlalchemy.bindparam('id'),
            'total_likes': Post.total_likes + sqlalchemy.bindparam('total_likes'),
            'live_ref_id': sqlalchemy.bindparam('live_ref_id')
        })
    results = await db.status(update_statement, update_values)

In my example, I want to atomically update the total_likes. Other fields are just bound to basic values.