0

I need to have modifed_at fields in my Django project. A field that updates every time a row updates in the database, despite where the update comes from: through calling .save() or through queryset.update() or even when updates happen in the database directly and not from the Django app.

there is an auto_now property that does not solve my problem according to this SO question(based on Django document). other SO questions(like this and this) ask the same thing, update instance at every change not only .save()

This problem can be solved using triggers as said here but this way we need to write the same trigger for every modifed_at field in models.

as discussed in this Django ticket this problem will not be addressed and solved in Django. even the suggested patch only updates the instance if it changes via Django.

the only way that comes to my mind is to do something like this in a mixin. a mixin that when inherited creates a trigger for fields with auto_now=True. maybe change SQL when Django creates the model creation SQL. but I don't know how to implement this.

so I have two questions:

  1. what is the best way to achieve database-level updates for modified_at fields
  2. If my suggested way is the best option how to implement it?

I would like to have a database-agnostic solution but FYI currently I'm using PostgreSQL.

Hosseinmp76
  • 317
  • 2
  • 12
  • Have you considered using a `post_save` signal? – Lewis Nov 11 '22 at 13:46
  • 1
    @Lewis `post_save` does not do the trick as documented: Finally, realize that update() does an update at the SQL level and, thus, does not call any save() methods on your models, nor does it emit the pre_save or post_save signals (which are a consequence of calling Model.save()). If you want to update a bunch of records for a model that has a custom save() method, loop over them and call save() https://docs.djangoproject.com/en/4.1/ref/models/querysets/#update – Hosseinmp76 Nov 11 '22 at 14:10

2 Answers2

0

As you said, if you use triggers you'd have to create a trigger for every table.

To make this easier however you could create a migration file to create/destroy the trigger. Here's an example.

Then it would just be a matter of copy-pasting that migration file whenever you create a new model.

Edit:

You could even override the makemigrations command to automatically add the creation of the trigger step to the operations of the initial migrations file. Here's an answer that shows how to override management commands.

Felix Eklöf
  • 3,253
  • 2
  • 10
  • 27
-1

So you can use the Django's auto_now field for that. If you want to auto populate the DateTimeField when creating a new object you can do this: created_at = models.DateTimeField(auto_now_add=True, verbose_name=_("created_at"))

If you want to update the field anytime the object is updated you can do this instead: updated_at = models.DateTimeField(auto_now=True, verbose_name=_("updated at"))

For more info you can refer here

kwamito
  • 284
  • 5
  • As I said in the question `auto_now` does not update when using `queryset.update` or when updating directly from the database not via Django. – Hosseinmp76 Nov 11 '22 at 14:05