tortoise / tortoise/tortoise-orm
`bulk_update()` ignores explicitly set values for `DatetimeField(auto_now=True)` and recalculates timestamp per object
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
When using bulk_update() with a DatetimeField(auto_now=True), Tortoise ignores the value already set on the model instance and recalculates a new timestamp, with microsecond difference, for each object.
This makes it impossible to assign a single deterministic timestamp across all rows in a bulk operation, even when the field is explicitly included in fields.
The behavior is surprising because the ORM silently overrides user-provided values.
To Reproduce
from tortoise import Tortoise, fields, models, run_async
from tortoise.timezone import now
class Item(models.Model):
id = fields.IntField(pk=True)
value = fields.IntField()
updated_at = fields.DatetimeField(auto_now=True)
async def reproduce():
await Tortoise.init(
db_url="sqlite://:memory:",
modules={"models": ["__main__"]},
)
await Tortoise.generate_schemas()
i1 = await Item.create(value=1)
i2 = await Item.create(value=2)
ts = now()
items = await Item.all()
for obj in items:
obj.value += 1
obj.updated_at = ts
await Item.bulk_update(items, fields=["value", "updated_at"])
i1_after = await Item.get(id=i1.id)
i2_after = await Item.get(id=i2.id)
assert i1_after.updated_at > i1.updated_at
assert i2_after.updated_at > i2.updated_at
# This should be True
assert i1_after.updated_at == i2_after.updated_at
run_async(reproduce())
Expected behavior
If a value is already set on the instance and the field is included in fields, bulk_update() should respect that value instead of recomputing it.
This would allow deterministic timestamps in bulk operations.
Additional context
Why this matters?
Bulk updates are typically used for performance-sensitive paths affecting thousands of rows.
In those cases it is common to want a single timestamp representing the update operation.
The current behavior forces users to either:
- run a second
Model.filter(...).update(...)to fix the timestamp, or - avoid auto_now=True entirely.
Environment:
- Tortoise ORM: 1.1.6
- Python: 3.14
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 at Item.bulk_update and trace how fields with auto_now are handled during the reproduced SQLite operation. Verify that an explicitly assigned updated_at is preserved for every object when it is included in fields, and that both rows retain the same timestamp after the bulk update.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python, sqlite
- Domain
- database
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 65/100