Skip to content

bulk_update() ignores explicitly set values for DatetimeField(auto_now=True) and recalculates timestamp per object #2133

@gGonz

Description

@gGonz

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions