maz

maz

Ecto Many-to-Many Association table does not append on insert?

I have Users and Orgs. Users can belong to multiple Orgs and Orgs can have many users:

defmodule Elijah.Schema.User do
  use Ecto.Schema
  import Ecto.Changeset
  use Waffle.Ecto.Schema

...
  schema "users" do
    field :email, :string
    field :username, :string
    field :name, :string
    field :phone_number, :string
    field :confirmed_at, :naive_datetime
    field :is_admin, :boolean
    field :avatar, Elijah.AvatarUploader.Type

    many_to_many :orgs, Elijah.Schema.Org, join_through: "orgs_users", on_replace: :delete

    timestamps()
  end
...

Org:

defmodule Elijah.Schema.Org do
  use Ecto.Schema
  import Ecto.Changeset
  alias Elijah.Type.OrgHashId
...
  schema "orgs" do
    field :basename, :string
    field :shortname, :string
    field :uuid, Ecto.UUID
    field :hash_id, :string

    has_many :channels, Elijah.Schema.Channel
    has_many :playlists, Elijah.Schema.Playlist
    many_to_many :users, Elijah.Schema.User, join_through: "orgs_users", on_replace: :delete

    timestamps(type: :utc_datetime)

    # timestamps()
  end
...

OrgsUsers:

defmodule Elijah.Schema.OrgsUsers do
  use Ecto.Schema
  import Ecto.Changeset


  schema "orgs_users" do
    field :org_id, :integer
    field :user_id, :integer
  end

  @doc false
  def changeset(orgs_users, attrs) do
    orgs_users
    |> cast(attrs, [:user_id, :org_id])
    |> validate_required([:user_id, :resource_id])
  end
end

I made a function called Accounts.add_user() which is meant to add a user to an org and update the orgs_users table. However what is happening is that the second association will clobber the first one:

Accounts.add_user():

    def add_user(%{"org_id" => org_id, "user_id" => user_id}) do
      org = Repo.get_by!(Org, id: org_id)
      user = Repo.get_by!(User, id: user_id)
      org
      |> Repo.preload(:users)
      |> Ecto.Changeset.change()
      |> Ecto.Changeset.put_assoc(:users, [user])
      |> Repo.update!
    end

Here is the log where I add two users:

SELECT * from orgs_users:

elijah_dev=# select * from orgs_users;
 id | org_id | user_id 
----+--------+---------
  2 |      1 |       2
(1 row)

Any idea why add_user() does not append? I also tried:

Ecto.Changeset.put_assoc(:users, org.users ++ user)

but resulted in this error:

** (ArgumentError) argument error
    :erlang.++(#Ecto.Association.NotLoaded<association :users is not loaded>, #Elijah.Schema.User<__meta__: #Ecto.Schema.Metadata<:loaded, "users">, avatar: nil, confirmed_at: nil, email: "amos@elijah.app", id: 1, inserted_at: ~N[2020-10-19 21:00:31], is_admin: nil, name: "Amos", orgs: #Ecto.Association.NotLoaded<association :orgs is not loaded>, phone_number: nil, updated_at: ~N[2020-10-19 21:00:31], username: "amos", ...>)
    (faithful_word 0.1.0) lib/faithful_word/accounts.ex:93: Elijah.Accounts.add_user/1

(first insert)

Most Liked

maz

maz

I finally got it working. I had to assign the preloaded org to an intermediate value:

      org_loaded = Repo.preload(org, [:users])
      org_changeset = Ecto.Changeset.change(org_loaded)
      org_user_changeset = org_changeset |> Ecto.Changeset.put_assoc(:users, [user | org_loaded.users])
      Repo.update!(org_user_changeset)

Not sure why it did not work with pipe operator.

fuelen

fuelen

Hello.

Yes, this behaviour is described in documentation to Ecto.Changeset.put_assoc/4:

This function is used to work with associations as a whole. For example, if a Post has many Comments, it allows you to add, remove or change all comments at once. If your goal is to simply add a new comment to a post, then it is preferred to do so manually, as we will describe later in the “Example: Adding a comment to a post” section.

The last error can be fixed by preloading users assoc to org:

org = Repo.preload(org, :users)

Where Next?

Popular in Questions Top

bsollish-terakeet
Credo is smart enough to check for (something like) this: assert length(the_list) == 0 with this response: Checking if an enum is empt...
New
pgiesin
This should be a simple problem but I just can’t seem to figure it out. I have a standalone Elixir app that won’t find the database. Dep...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
sacepums
Hey guys. I'm new to elixir and im really stocked about it. But I ran into a bit of problem - I need to convert a date sting, for examp...
New
aadeshere1
I have a another noob question about loop. Since elixir is immutable, while loop is not directly possible. total = 10 while total != 0 ...
New
alice
Hey, Just curious what are the main benefits of Elixir compared to Clojure? When is Elixir more useful than Clojure and vice versa? Th...
New
chewm
Hi guys, nice to meet you to the whole forum, I’m new here, I’m trying to configure visual studio code for elixir, right now the intellis...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New
skosch
To my knowledge, put_in, Map.update etc. all have the one limitation of not automatically creating intermediate keys when needed (for exa...
New
vrod
I am using the Starship cross-shell prompt – it seems pretty nice, but I get some errors: [WARN] - (starship::utils): Executing command ...
New

Other popular topics Top

aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
fireproofsocks
I’m working on defining a simple Ecto schema for a table (in PostGres), but I don’t see where I can define a column as NOT NULL. Conside...
New
lk-geimfari
What is most correct way to open, read and parse JSON file with poison? For example if we have example.json file in root of some projec...
New
hariharasudhan94
I would like to know what is the best IDE for elixir development?
New
lastday4you
I wanted to check elixir version in phoenix because i found that my elixir is 1.5 but when i use Enum.chunk_by it said the function is un...
New
mgjohns61585
Could someone help me? I'm making my first elixir program, number guessing game. I can't figure out how to convert the user's guess from ...
New
qwerescape
Is there a way to get the call stack or stack trace at any point in the code? Not from exceptions, but an expression that returns how the...
New
electic
Hi, I am new to Elixir. I am trying to use the DateTime component to insert a date into MySQL however the there seems to be no way to fo...
New
9mm
I am constructing a JSON object (map) and I need to conditionally set a field. I’m trying to write proper elixir-way code… and I’m at a l...
New

We're in Beta

About us Mission Statement