Reputation: 861
An organization
has many users
schema "organizations" do
field :name, :string
has_many :users, TestApp.User
end
A user
has many subordinates
schema "users" do
field :name, :string
belongs_to :organization, TestApp.Organization
belongs_to :manager, TestApp.User,
foreign_key: :manager_id
has_many :subordinates, TestApp.User,
foreign_key: :manager_id
end
How do I ensure subordinates have an organization_id
value when they are created in the following manner?
test "create Org and User and Subordinate in one step" do
subordinate =
%User{}
|> User.changeset(%{name: "A Subordinate"})
manager =
%User{}
|> User.changeset(%{name: "A Manager"})
|> Changeset.put_assoc(:subordinates, [subordinate])
organization =
%Organization{}
|> Organization.changeset(%{name: "An Organization"})
|> Changeset.put_assoc(:users, [manager])
%{users: [ %{subordinates: [subordinate]} = manager]} = organization = Repo.insert!(organization)
# Passes
assert manager.organization_id == organization.id
# Fails
assert subordinate.organization_id == organization.id
end
Upvotes: 3
Views: 164
Reputation: 51
This is a late response, but for anyone who is struggling with creating nested records on Phoenix Ecto. can make use of Ecto.Multi's run or merge function.
You can read more in the official documentation here.
For this problem, you can use this snippet:
Ecto.Multi.run or "Ecto.Multi.merge"
Ecto.Multi.new()
|> Ecto.Multi.insert(:organization, Organization.create_changeset(%Organization{}, organization_params))
|> Ecto.Multi.insert(:subordinate, fn %{organization: organization} ->
User.create_changeset(%User{}, %{
name: "A Manager",
organization_id: organization.id
}))
|> Ecto.Multi.run(:manager, fn _repo, %{subordinate: subordinate} ->
Users.create_changeset(
name: "A Subordinate",
subordinate_id: organization.id
)
|> Ecto.build_assoc(:subordinate)
|> Repo.insert
end)
|> Repo.transaction
Upvotes: 0
Reputation: 417
Option1
Invoke Repo.insert! on every changeset separately
subordinate =
%User{}
|> User.changeset(%{name: "A Subordinate"})
|> Repo.insert!
manager =
%User{}
|> User.changeset(%{name: "A Manager"})
|> Changeset.put_assoc(:subordinates, [subordinate])
|> Repo.insert!
Create helper function that returns list of managers merged with their subordinates
defp managers_with_subordinates(managers) do
Enum.reduce(managers, [], &(&2 ++ [&1] ++ &1.subordinates))
end
then pass result to put_assoc
users = managers_with_subordinates([manager])
organization =
%Organization{}
|> Organization.changeset(%{name: "An Organization"})
|> Changeset.put_assoc(:users, users)
and change your pattern matching to
%{users: [user1, user2]} = organization = %Organization{} |> Organization.changeset |> put_assoc(:users, x) |> Repo.insert!
assert user1.organization_id == organization.id
assert user2.organization_id == organization.id
assert user2.manager_id == user1.id
Option2
subordinate =
%User{}
|> User.changeset(%{name: "A Subordinate"})
|> prepare_changes( fn(changeset) ->
manager = changeset.repo.get(User, changeset.changes.manager_id)
changeset |> cast(%{organization_id: manager.organization_id}, [:organization_id])
)
Upvotes: 1