Mark Karavan
Mark Karavan

Reputation: 2684

Duplicating entries with associations in elixir

My blog app has a feature that allows people to make copies of other people's posts, and host them as their own.

Posts are associated with owners.

  schema "posts" do 
    field :title, :string 
    field :content, :string 

    belongs_to :owner, MyApp.Accounts.User, foreign_key: :owner_id
  end 

I would like to get a copy of a Post by id, remove the id and owner_id fields, cast the new owner id, and then insert.

resolve fn (%{post_id: post_id, new_owner_id: new_owner_id}, _info) ->
  Repo.get!(Post, post_id)
    |> Map.delete(:id)
    |> Map.delete(:owner_id)
    |> Post.changeset(%{owner_id: new_owner_id})
    |> Repo.insert!
end

But when I do, I get this error:

Request: POST /api
** (exit) an exception was raised:
    ** (RuntimeError) attempting to cast or change association `owner` from `MyApp.Content.List` that was not loaded. Please preload your associations before manipulating them through changesets

What is the correct way to do this copy?

Upvotes: 0

Views: 489

Answers (1)

Aleksei Matiushkin
Aleksei Matiushkin

Reputation: 121010

As it was clearly mentioned in the error message:

Please preload your associations before manipulating them through changesets

resolve fn (%{post_id: post_id, new_owner_id: new_owner_id}, _info) ->
  Post
  |> Repo.preload(:owner) # ⇐ this
  |> Repo.get!(post_id)
  |> Map.delete(:id)
  |> Map.delete(:owner_id)
  |> Post.changeset(%{owner_id: new_owner_id})
  |> Repo.insert!
end

Sidenote: I believe, you are doing it wrong. The better design would be to reflect the possible future changes in the “cited” post. Instead of copying it’s content, I would rather introduce the new many-to-many relationship between users and posts, called e.g. “copies.”

Copy by reference is better than copy by value. DRY principle is the one of very few that work always.

Upvotes: 1

Related Questions