Enum.map on put_assoc

So, say I have a Chapter struct with contents association, like so:

%Chapter{
    contents [
        %Content {
            title: "some title",
            deleted: false
        },
        %Content {
            title: "some other title",
            deleted: false
        },
    ]
}

Then when I delete (soft delete) a chapter I want it to propagate to all the contents.

My first approach was to do something like:

chapter
|> change(deleted: true)
|> change()
|> put_assoc(:deleted, chapter.deleted |> Enum.map(&( %Chapter{&1 | deleted: true}))
|> Repo.update()

The changeset goes through but the changes get to the othe side empty.

Could anyone help me understand why it doesn’t work and, if possible, guide me to how I’d go about achieving it?

Thanks :slight_smile:

Couple odd things with this line:

  • two lines previous, the changeset put true in deleted; now this is put_associng it. That doesn’t seem correct - did you mean put_assoc(:contents, ...?

  • similarly with chapter.deleted |> Enum.map: how is deleted declared in the Chapter schema?

  • finally, inside the map is updating structs with %Chapter{} but the example shows the data should be %Content{}

2 Likes

Hi!

So:

My intention here was to change the deleted value to true in Chapter (deleting the chapter) and then change the value of deleted to true for all its Content with put_assoc by passing the original value mapped to replace deleted: false to true. Am I understanding it wrong?

This is wrong, sorry! It should be chapter.contents

Also wrong… heh :persevere:

It should all be:

chapter
|> change(deleted: true)
|> put_assoc(:contents, chapter.contents |> Enum.map(&( %Content{&1 | deleted: true}))
|> Repo.update()

But it still doesn’t work.

Thanks for taking your time reading this mess!

Hi,

When you associate the %Content{} with the %Chapter{} in the schema you set the behaviour for deletions:

has_many :content, Content, on_delete: :delete_all

When you delete a %Chapter{} it will delete the content.

If you want to retain the %Content{} when it is not associated with the %Chapter{} you will need to post how you have set up the associations.

Hope this helps.

Andrew

1 Like

Thanks for your answer!

This was the approach I adopted before, but the thing is, I didn’t want to lose these data for good so I’m not actually deleting these items from the database but only marking them as deleted and hiding it from the user (soft deletion), so what I need is really to just change those values!

One way you can achieve this through cast_assoc.

chapter
|> change(deleted: true)
|> cast_assoc(:contents, required: false, with: &soft_deletion_changeset/2)

You can implement this inner changeset any way you see fit (I’d recommend having it in your Content schema for consistency sake) but it’s important for the arity to be 2, even if you don’t use both:

def soft_deletion_changeset(content, _attrs) do 
  content
  |> change(%{deleted: true})
end
1 Like

Yes, that’s exactly it!
Thank you so much!!!