Let's say I have these schemas:
defmodule Sample.Post do
use Ecto.Schema
schema "post" do
field :title
has_many :comments, Sample.Comment
end
end
defmodule Sample.User do
use Ecto.Schema
schema "user" do
field :name
has_many :comments, Sample.Comment
end
end
defmodule Sample.Comment do
use Ecto.Schema
schema "comment" do
field :text
belongs_to :post, Sample.Post
belongs_to :user, Sample.User
end
end
My questions is how can I use Ecto.build_assoc
to save a comment?
iex> post = Repo.get(Post, 13)
%Post{id: 13, title: "Foo"}
iex> comment = Ecto.build_assoc(post, :comments)
%Comment{id: nil, post_id: 13, user_id: nil}
So far it's ok, all I need to do is use the same function to set the user_id
in my Comment
struct, however since the return value of build_assoc
is Comment
struct, I can not use the same function
iex> user = Repo.get(User, 1)
%User{id: 1, name: "Bar"}
iex> Ecto.build_assoc(user, :comment, comment)
** (UndefinedFunctionError) undefined function: Sample.Comment.delete/2
...
I have two options but neither of them looks good to me:
First one is to set user_id
manually!
iex> comment = %{comment| user_id: user.id}
%Comment{id: nil, post_id: 13, user_id: 1}
Second one is to convert the struct to map and ... I don't even want to go there
Any suggestion?
Why don't you want convert struct to map? It is really easy.
build_assoc
expects map of attributes as last value. Internally it tries to delete key :__meta__
. Structs have compile time guarantees, that they will contain all defined fields, so you are getting:
** (UndefinedFunctionError) undefined function: Sample.Comment.delete/2
But you can just write:
comment = Ecto.build_assoc(user, :comment, Map.from_struct comment)
and everything will work fine.
Just pass it along with build_assoc
iex> comment = Ecto.build_assoc(post, :comments, user_id: 1)
%Comment{id: nil, post_id: 13, user_id: 1}
Check here for more details.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With