tortoise/tortoise-orm

UPDATE the query updates all the data inside the entity

DTPlayer opened this issue · 2 comments

Describe the bug
When sending .save() (inside any context) UPDATE query updates all data inside the selected entity, not just those specified.

To Reproduce
An example of a model for interaction:

class User(Model):
    id = fields.BigIntField(pk=True)
    first_name = fields.CharField(max_length=255)
    balance = fields.FloatField(default=0)
    is_admin = fields.BooleanField(default=False)
    referral = fields.ForeignKeyField('models.User', null=True)
    referral_percent = fields.IntField(default=5)

And we are trying to update the user's balance:

user = await User.get(id=1) # an example of getting an entity
user.balance = 1200
await user.save()

And inside the logs we see the UPDATE of all data (even those that we have not touched)

tortoise.db_client - DEBUG - UPDATE "user" SET "first_name"=?,"balance"=?,"is_admin"=?,"referral_percent"=?,"referral_id"=? WHERE "id"=?: ['test', 1200.0, 1, 5, None, 1] - /home/codespace/.local/lib/python3.12/site-packages/tortoise/backends/sqlite/client.py:139

Expected behavior
Expected to see a format request:

tortoise.db_client - DEBUG - UPDATE "user" SET "balance"=? WHERE "id"=?: [1200.0, 1] - /home/codespace/.local/lib/python3.12/site-packages/tortoise/backends/sqlite/client.py:139

Additional context
This problem can be critical in a situation where the task is to overwrite 2-3 fields in a table without waiting, and we want (from another thread) to change the data inside this table for the same entity, which causes an UPDATE once a second.
The solution to the problem at the moment is to write a direct SQL query inside in_transaction.

user_id = 1 
amount = 1200
# user = await User.get(id=user_id)
# user.balance = amount
# await user.save()
async with in_transaction() as conn:
    await conn.execute_query('UPDATE users SET balance = ? WHERE id = ?', [amount, user_id])

And get this in log:

aiosqlite - DEBUG - operation functools.partial(<bound method Connection._execute_fetchall of <Connection(Thread-1, started 133812847179520)>>, 'UPDATE "user" SET "balance" = ? WHERE "id" = ?', [1200.0, 1]) completed - /home/codespace/.local/lib/python3.12/site-packages/aiosqlite/core.py:103

It has been tested on: sqlite, postgres

You can specify which fields to update in the following way: await user.save(update_fields=("balance",)), see the docs.

You can specify which fields to update in the following way: await user.save(update_fields=("balance",)), see the docs.

Thank you, I did not know that there is such a thing inside the documentation.