tortoise / tortoise/tortoise-orm
save(force_update=True) error in specific cases
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 5.6k
- Forks
- 516
- Avg merge
- 2d 21h
- Merged PRs (30d)
- 9
Description
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)
- 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__"]})
- 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')
- 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'>
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in tortoise/models.py around the save method at line 948, then follow execute_update for the force_update path. Reproduce the example with MySQL or MariaDB and compare it with the provided SQLite case and the differing rows values. Done means saving an instance loaded with User.get(id=1) succeeds with force_update=True when the database row exists.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- mysql, python
- Domain
- databases
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Stale
- Clarity
- Mostly clear
- Newbie friendliness
- 45/100