Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ecto association to more than one schemes

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?

like image 805
slashmili Avatar asked Jan 16 '16 03:01

slashmili


2 Answers

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.

like image 134
tkowal Avatar answered Nov 13 '22 08:11

tkowal


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.

like image 32
Siva Avatar answered Nov 13 '22 10:11

Siva