tortoise-orm icon indicating copy to clipboard operation
tortoise-orm copied to clipboard

save(force_update=True) error in specific cases

Open maou-shonen opened this issue 2 years ago • 0 comments

Describe the bug Use save(force_update=True) after getting an instance will cause an unexpected error

To Reproduce

from tortoise import Tortoise, fields, run_async
from tortoise.models import Model

class User(Model):
    id: int = fields.IntField(pk=True)
    name: str = fields.CharField(max_length=255)

async def main():
    await Tortoise.init(db_url="mysql://test@localhost/test", modules={"models": ["__main__"]})
    await Tortoise.generate_schemas()

    # create a user, not important here
    await User.create(name='111')

    user = await User.get(id=1)
    user.update_from_dict(dict(name='222'))
    await user.save(force_update=True)  # raise exception

if __name__ == '__main__':
    run_async(main())
  File "/app/tortoise-bug/.venv/lib/python3.10/site-packages/tortoise/models.py", line 948, in save
    raise IntegrityError(f"Can't update object that doesn't exist. PK: {self.pk}")
tortoise.exceptions.IntegrityError: Can't update object that doesn't exist. PK: 1

Expected behavior Should working

Additional context

# my test environment
python version: 3.10
tortoise-orm version: 0.18.1 and 0.19.2
mysql: mariadb 10.8.3 and mysql 5.8 (GCP)
  1. DB engine uses the sample sqlite without error
--- await Tortoise.init(db_url="mysql://test@localhost/test", modules={"models": ["__main__"]})
+++ await Tortoise.init(db_url="sqlite://:memory:", modules={"models": ["__main__"]})
  1. The instance is not from get will without error
--- await User.create(name='111')
--- user = await User.get(id=1)
+++ user = await User.create(name='111')
  1. save method does not use the force_update parameter without error
--- await user.save(force_update=True)  # raise exception
+++ await user.save()

in "/home/shonen/tortoise-bug/.venv/lib/python3.10/site-packages/tortoise/models.py", line 948

    elif force_update:
        rows = await executor.execute_update(self, update_fields)

        # my test
        assert rows==1, f'{rows=} {type(rows)=}'

        if rows == 0:
            raise IntegrityError(f"Can't update object that doesn't exist. PK: {self.pk}")
        created = False
AssertionError: rows=0 type(rows)=<class 'int'>

In addition, an unbelievable result occurred in my company's environment (with FastAPI) (but not successfully reproduced in the minimal example)

    elif force_update:
        rows = await executor.execute_update(self, update_fields)

        # my test
        assert rows==1, f'{rows=} {type(rows)=}'
        raise Exception(f'{rows=} {type(rows)=}')

        if rows == 0:
            raise IntegrityError(f"Can't update object that doesn't exist. PK: {self.pk}")
        created = False
File "/app/tortoise-bug/.venv/lib/python3.10/site-packages/tortoise/models.py", line 944, in save
    raise Exception(f'{rows=} {type(rows)=}')
Exception: rows=1 type(rows)=<class 'int'>

maou-shonen avatar Aug 03 '22 10:08 maou-shonen