I have a situation in which I have two models with a belongs_to between them. As
an example, I’ll use the typical Post -> Author models:
defmodule Post
schema "posts" do
field :title, :string
belongs_to :author, User
end
end
defmodule User
schema "users" do
field :name, :string
end
end
Now I have an instance of a Post, and want to change the author of that Post.
The author I want to update the Post to I have in memory, and the current author
relation is not preloaded.
def change_author(%post{} = post, %user{} = author)
post
|> Post.update_changeset(%{author_id: author.id})
|> Repo.update()
end
with the following changeset function:
defmodule Post
# ... schema ..
def update_changeset(post, attrs) do
post
|> cast(attrs, [:author_id])
|> foreign_key_constraint(:author_id)
end
end
The problem with this approach is that the resulting Post
model returned by
the Repo.update
call doesn’t have the updated User
instance.
Another option would be to use put_assoc
instead of using the author_id
key
directly, but when using put_assoc
Ecto requires the related resource (author
in this case) to be preloaded, which requires an extra database call for a
resource that I don’t need.
For now I can solve this with something like:
def change_author(%post{} = post, %user{} = author)
post
|> Post.update_changeset(%{author_id: author.id})
|> Repo.update()
|> case do
{:ok, %Post{} = post} ->
{:ok, Map.put(post, :author, author)}
{:error, changeset} ->
{:error, changeset}
end
end
… but that extra case
just to manually add the relationship again doesn’t
seem very clean.
Is there a better way of solving this wihtout performing needless database calls?