Warning: I am a novice at Elixir, Phoenix, and Ecto. I'll update this post as I am corrected. Note also that this applies to Phoenix 1.2.0 and Ecto 2.0.2.

Updated: July 11. There is no need for a join table model. See also this post from OvermindDL1.

Phoenix and Ecto have recently (I gather) gotten explicit support for many-to-many associations (via join tables). I found that making them work was uncharacteristically hard, hence this blog post.

Note: my job was probably made harder because I didn't want to make the join table a first class model object with a primary key and changesets and all that. That is, I was working from this bit of documentation:

don't refer to model objects

What I wanted

There are users and organizations. Organizations have many users and users can belong to many organizations.


You can find the source behind this post at Github.

Table creation

This is the same as what the documentation says. The migration looks like this:

  def change do
    create table(:users_organizations, primary_key: false) do
      add :user_id, references(:users, on_delete: :delete_all)
      add :organization_id, references(:organizations, on_delete: :delete_all)

Save yourself grief: end the fields with _id.

The model

Each of the User and Organization models describe the many-to-many relationship. (Note that the name of the column in the join table is inferred.)

defmodule Eecrit.User do
  use Eecrit.Web, :model

  schema "users" do
    many_to_many :organizations, Eecrit.Organization, join_through: "users_organizations"
defmodule Eecrit.Organization do
  use Eecrit.Web, :model

  schema "organizations" do
    many_to_many :users, Eecrit.User, join_through: "users_organizations"

You do not need an Eecrit.UsersOrganizations model.

What now works (not this blog engine's syntax highlighting, as it turns out)

See also seed.exs.