Elixir / Ecto Embedded struct - updated_at not getting updated
Asked Answered
C

1

10

I have an Elixir struct with an embedded struct, following the instructions from José Valim himself:

http://blog.plataformatec.com.br/2015/08/working-with-ecto-associations-and-embeds/

    defmodule MyApp.Post do
      use Ecto.Schema
    
      schema "posts" do
        field :title
        field :body
        embeds_many :comments, MyApp.Comment
        timestamps
      end
    end
    
    defmodule MyApp.Comment do
      use Ecto.Schema
    
      embedded_schema "comments" do
        field :body
        belongs_to :post, MyApp.Post
        timestamps
      end
    end

Almost everything works perfectly, with the exception of the updated_at timestamp of the embedded Comments, which seem to retain their original value after being updated.

What I mean is, if I change one of the Comments in the list and update it on the Post and save it like this:

my_post
|> change()
|> put_embed(:comments, comments)
|> Repo.update()

the update to the values works as one would expect, except the updated_at timestamp of the Comment doesn't change.

I can set it manually:

|> Map.put(:updated_at, DateTime.to_iso8601(DateTime.utc_now))

and that works, but trying to figure out why it's not being automatically set by Ecto. Using Ecto 3.5.3. Any help would be appreciated!

Migration:

defmodule MyApp.Repo.Migrations.CreatePosts do
    use Ecto.Migration

    def change do
        create table(:posts) do
            add :title, :string
            add :body, :string
            add :comments, :map
            
            timestamps()
        end
    end
end
Cruiser answered 21/12, 2020 at 1:57 Comment(5)
Can you post the migrationBrandebrandea
Is it possible you've configured the embedded schema to not have a primary key? The lack of a primary key would prevent it from realizing the same record was being updated, and cause it to treat the record as new.Crack
The embedded structs appear to get a cryptic looking generated key (for example 23d7409a-6028-4fe5-b97c-5c2af3706b3a), and I am able to update and delete them using that key. The full CRUD functionality works perfectly for the embedded records, with the one exception of the updated_at timestamp not being updated.Cruiser
Does the inserted_at value change?Verret
Yes it has a primary key by default, that's how existing embedded records can be updated. The inserted_at value does not change.Cruiser
M
1

put_embed/4's behaviour is based on that of put_assoc/4, which states that:

a map or keyword list can be given to update the associated data as long as they have matching primary keys. For example, put_assoc(changeset, :comments, [%{id: 1, title: "changed"}]) will locate the comment with :id of 1 and update its title. If no comment with such id exists, one is created on the fly.

Based on the behaviour that you're commenting, I would suspect that you're not providing the :id field in the comments. This means that existing comments get deleted and the provided comments get inserted. To validate this hypothesis, check if the inserted_at or the id field changes with each execution.

Manlove answered 20/5, 2021 at 12:41 Comment(2)
I am providing the ID. It is updating the fields in the correct comment(s) as one would expect, the only thing it's not doing is updating the updated_at.Cruiser
@Cruiser Hm... at this stage then I'd suggest you provide a reproducible snippet, otherwise it's a guessing game.Myer

© 2022 - 2024 — McMap. All rights reserved.