Reputation: 525
I have a User and Program model which have a many-to-many association. They create associations successfully, so that a User has many Programs (and vice versa), but now I need to remove the association (but not delete the User or Program)
I have a function in the ProgramController that associates them like so:
# Myapp.ProgramController
def associate_user(conn, _params) do
%{"_csrf_token" => _csrf_token,"_method"=>_method,"_utf8" => _utf8,"id" => id, "user" => %{"email" => email}} = _params
{pid, _} = Integer.parse(id)
user = Repo.get_by(User, email: email) |> Repo.preload(:programs)
program = Repo.get_by(Program, id: pid) |> Repo.preload(:users)
pid_list = user.programs
|> Enum.map(fn x -> x.id end)
|> List.insert_at(0, program.id)
changeset_list = from(program in Myapp.Program, where: program.id in ^pid_list)
|> Repo.all
|> Enum.map(&Ecto.Changeset.change/1)
from(user in User, where: user.email == ^user.email, preload: [:programs, :role])
|> Repo.one
|> User.changeset(%{})
|> Ecto.Changeset.put_assoc(:programs, changeset_list)
|> Repo.update!
program = conn.assigns[:program]
changeset = Program.changeset(program)
conn
|> put_flash(:info, "Program updated successfully.")
|> redirect(to: program_path(conn, :edit, program))
end
and I have a migration that creates a join table
defmodule Myapp.Repo.Migrations.AssociateUsersAndPrograms do
use Ecto.Migration
def change do
alter table(:users) do
add :current_program_id, references(:programs, on_delete: :nilify_all)
end
create table(:users_programs, primary_key: false) do
add :user_id, references(:users, on_delete: :delete_all)
add :program_id, references(:programs, on_delete: :delete_all)
end
create index(:users_programs, [:user_id])
end
end
How would I go about disassociating a particular user from a program as I did not see a delete_assoc in the Ecto.Docs?
Still learning and loving Elixir (and Phoenix) so far so any help would be much appreciated!
Upvotes: 6
Views: 5565
Reputation: 742
Check out the on_replace property on Ecto's documentation. You just have to mark the field with the on_replace property and to call Ecto.Changeset.put_assoc as you have been doing, something like this:
schema "users" do
...
many_to_many :programs, Program, join_through: "users_programs", on_replace: :delete
...
end
It isn't very clear on the docs.
Upvotes: 3
Reputation: 4251
You'll probably want to look at the Ecto.Schema.ManyToMany docs. There are a few options (taken and reformatted directly from docs):
:on_delete
- The action taken on associations when the parent record is deleted.:nothing
(default):delete_all
- will only remove data from the join source, never the associated records. Notice :on_delete may also be set in migrations when creating a reference. If supported, relying on the database via migrations is preferred. :nilify_all
and :delete_all
will not cascade to child records unless set via database migrations.Upvotes: 7