Using the same database timestamp for saving two objects

I’m going to save two objects, and since one is essentially a ledger for the other, it should have the same database assigned timestamps. I want the equivalent to this:

BEGIN;
update accounts 
set created_at = CURRENT_TIMESTAMP
where id = 1;
update ledger
set created_at = CURRENT_TIMESTAMP
where account_id = 1;
COMMIT;

Which makes sure that both created records have the same timestamp. How do accomplish this?

I tried using transaction.atomic and setting up auto_now on both fields, but the timestamps don’t match:

>>> with transaction.atomic():
...   tr.save()
...   trs.save()
... 
>>> tr.created_at
datetime.datetime(2025, 1, 3, 13, 36, 18, 989087, tzinfo=datetime.timezone.utc)
>>> trs.created_at
datetime.datetime(2025, 1, 3, 13, 36, 18, 989768, tzinfo=datetime.timezone.utc)
>>> 

So, that’s not an option.

That’s correct - the datetime field is set in Django in the pre_save method for fields using auto_now, it’s not set as a database function in the SQL.

If you need to coordinate the timestamp among multiple objects, the only way that I’m aware of being able to do it is by setting the fields manually - which implies either not using auto_now, or overriding one of the relevent functions to set it.

I was aware of that, but I wanted to avoid having to calculate it in my application, since timestamp order is important and I wouldn’t want the application to have control of it. Is there a way that I can do what I’m doing with SQL with standard Django api’s?

I’m also aware of using a cursor to get the timestamp from the database, but that would imply a round trip and by the time it’s recorded the timestamp would be outdated.

Hi Bariam

I’d look at adding a custom F expression. https://docs.djangoproject.com/en/5.1/ref/models/expressions/#f-expressions

As long as auto_now or auto_add_now are not false, if you try to pass anything when creating the object that seems to be a datetimefield, Django will replace it with datetime.now(). Instead, you should make sure that you define it manually when saving objects:

# models.py
class BalanceList(model.Models):
  created_at = models.DateTiemField()
  ...
class Ledger(model.Models):
  created_at = models.DateTiemField()
  ...

# views.py
b = BalanceList(created_at=TransactionNow(), ...)
l = Ledger(created_at=TransactionNow(), ...)

with transaction.atomic():
  b.save()
  l.save()

This will guarantee that it has the same timestamp:

>>> b.created_at
TransactionNow()
>>> b.refresh_from_db()
>>> b.created_at
datetime.datetime(2025, 1, 3, 18, 39, 12, 485236, tzinfo=datetime.timezone.utc)
>>> l.refresh_from_db()
>>> l.created_at
datetime.datetime(2025, 1, 3, 18, 39, 12, 485236, tzinfo=datetime.timezone.utc)
>>> 

The only inconvenience with this, is that full_clean() won’t pass.