You have a couple different approaches here.
The simplest approach, if you are receiving a list of role_ids from the client, is to do this:
user
|> Ecto.Changeset.cast_assoc(params, [:name, ...])
|> Ecto.Changeset.put_assoc(load_roles(params))
def load_roles(params) do
case params["role_ids"] || [] do
[] -> []
ids -> Repo.all from r in Role, where: r.id in ^ids
end
Another approach is to use a has_many :through
. That’s because you don’t want to insert roles but you want to insert the association between a role and a user. So assuming this structure:
create table(:users) do
...
end
create table(:user_roles) do
add :user_id, references(:users)
add :role_id, references(:roles)
end
create table(:roles) do
end
And the schemas:
defmodule MyApp.User do
use Ecto.Schema
schema "users" do
has_many :roles, through: [:user_roles, :role]
has_many :user_roles, MyApp.UserRole
...
end
end
defmodule MyApp.UserRole do
use Ecto.Schema
schema "user_roles" do
belongs_to :user, MyApp.User
belongs_to :role, MyApp.Role
end
end
defmodule MyApp.Role do
use Ecto.Schema
schema "roles" do
...
end
end
You can now associate many roles to a user by using user |> cast(...) |> cast_assoc(:user_roles)
as long as the parameters are sent in the following shape:
%{
"user" => %{
"user_roles" => [
%{"role_id" => 1},
%{"role_id" => 3},
%{"role_id" => 5},
]
}
}
Btw, we cover those cases in the Ecto 2.0 book, so if you haven’t grabbed your copy yet, you definitely should: http://pages.plataformatec.com.br/ebook-whats-new-in-ecto-2-0