Search code examples
elixirphoenix-frameworkectoseeding

Seeding associations made with phx.gen.json


Using Ecto v2.2.6, Phoenix 1.3

I am trying to seed a linking table created with phx.gen.json.

First, a simple linking table. I create a User model, a Groups model, and a linking table that joins them with a many-to-many relationship.

Primary tables:

mix phx.gen.json Account User users email:string
mix phx.gen.json Account Group groups name:string

For users_groups, I create a migration and a schema without phx.gen

Migration:

create table(:users_groups) do
  add :user_id, references(:users)
  add :group_id, references(:groups)

  timestamps()
end
# indexes here

Schema:

  schema "users_groups" do
    belongs_to :user, MyApp.Accounts.User
    belongs_to :group, MyApp.Accounts.Group

    timestamps()
  end

I add many-to-many relationships with the User and Group schemas, and then I seed them like this:

Repo.insert!(UsersGroups.changeset(
  %UsersGroups{}, %{
    user_id: 1,
    group_id: 2
  }
))

It works great; the associations go in and I can see my many-to-many relationships.

However, if I create the linking table with something like this:

mix phx.gen.json Accounts UserGroup users_groups user_id:references:users group_id:references:groups

...and try the same approach to seeding, I see this:

%MyApp.Accounts.UserGroup{__meta__: #Ecto.Schema.Metadata<:loaded, "users_groups">,
  id: 1, inserted_at: ~N[2017-10-07 19:36:34.095408],
  group: #Ecto.Association.NotLoaded<association :group is not loaded>,
  group_id: nil, updated_at: ~N[2017-10-07 19:36:34.095411],
  user: #Ecto.Association.NotLoaded<association :user is not loaded>,
  user_id: nil},

What is happening, and what do I have to do to seed this new entity?


Solution

  • By default, the generated changeset functions do not include associations in the cast call 1 2 3, which means they're ignored by the changeset function. You'll need to add these columns yourself to the cast call.

    In lib/my_app/accounts/user_group.ex, change:

    def changeset(%UserGroup{} = user_group, attrs) do
      user_group
      |> cast(attrs, [])
      |> validate_required([])
    end
    

    to:

    def changeset(%UserGroup{} = user_group, attrs) do
      user_group
      |> cast(attrs, [:group_id, :user_id])
      |> validate_required([:group_id, :user_id])
    end
    

    and the seed function should now work.