How can I setup "timestamps()" so that it inserts "nil" by default in updated_at?

How can I set up “timestamps()” so that it inserts “nil” by default in updated_at?

2 Likes

Nobody knows?

Check out the autogenerate option: https://hexdocs.pm/ecto/Ecto.Schema.html#timestamps/1

I think you have to work with that manually.

I’m afraid that you can’t do that according to this note.

In theory you could send option like this: timestamps(updated_at: false) which wouldn’t create this column and you could insert it with different type than any datetime e.g. add :updated_at, :string, but it would be over engineering.

If you want to track whether struct was updated, you have several options to do that, but each of them is related to process data from DB, but not do any operation directly on DB level:

  • compare inserted_at with updated_at
  • add custom flag is_updated? or something similar

I hope it will help you.

But that will adjust both columns - updated and inserted whereas I need only “updated”.

Check the args given to that function if you can differentiate based on them. Otherwise I think you will need to do it by yourself without timestamps.

Thread necro apologies…

Adding a comment for anyone who tried to follow the helpful link posted by @PatNowak. It’s since moved to ecto_sql.

I was wondering about this question as well and based on that link my understanding of the default behaviour is as follows:

Ecto.Migration.timestamps/1 does not allow you to create nullable inserted_at or updated_at fields. It only allows you to disable each of these fields if required.

Based on this Ecto.Schema.timestamps/1 must assume that updated_at is a non-nullable field so it always provides a value.

I think this makes sense, if not feel free to correct.

I looked at the autogenerate option but as best as I can tell you need to supply a zero-arity function so there’s no context to determine if the database operation is an insert or update, so that doesn’t provide extra functionality.

I’ve seen this approach implemented:

In theory you could send option like this: timestamps(updated_at: false) which wouldn’t create this column and you could insert it with different type than any datetime e.g. add :updated_at, :string, but it would be over engineering.

Of course, a big drawback with that creating the update data is now a userland concern and a developer has to remember that. I am not a big fan of the default behaviour but it’s definitely the far lesser of two evils in this case imho.

Personally I would really love to see this functionality be configurable to insert nil but I don’t know if there’s much appetite for that and I can live with the default behaviour.

Anyway, there doesn’t seem to be a lot of discussion (that I could find) about this behaviour so I thought it would be worth adding a comment here.

2 Likes

Am I’m wrong in thinking the only reason someone would want this is so they can know if something has never been updated before? If so, that’s where inserted_at = updated_at. An index can be created for the times it’s an actual business concern. Otherwise, anywhere else you care about updated_at will require a null check.

2 Likes

The other motivation that comes to mind is simply conserving disk space, since updated_at tends to be on most tables (and a COALESCE(updated_at, inserted_at) yields the same information without duplication of data).

FWIW I’ve worked with CDC tools that all but required updated_at IS NULL for unedited records, so while this is a reasonable work-around I still think there’s value in this behaviour being configurable.

3 Likes

Oh, fair enough! The type of answer I was looking for :slight_smile:

That feels kinda strange tbh. Why would they care for “has been updated or not” especially over “has been updated since last checked”? Like the first edit should be no more special over any subsequent edit.

:person_shrugging: I don’t remember why they cared (or which CDC solution this was, years ago) but we ended up not going with them because of other similarly confusing warts