roganjoshua
I have a many_to_many relationship ( I guess there really isn’t such a thing) but a junction table nevertheless.
erDiagram
USER_GAMES {
int user_id
int game_id
}
USER {
int id
string name
}
GAME {
int id
string name
}
schema "users" do
field :email, :string
many_to_many :games, Game, join_through: "users_games"
timestamps(type: :utc_datetime)
end
schema "games" do
field :name, :string
field :state, :map
field :started_at, :utc_datetime
many_to_many :users, User, join_through: "users_games"
timestamps(type: :utc_datetime)
end
No schema for users_games but in the migration
create table("users_games", primary_key: false) do
add :user_id,
references(:users, on_delete: :delete_all),
null: false
add :game_id,
references(:games, on_delete: :delete_all),
null: false
timestamps()
end
create unique_index(:users_games, [:user_id, :game_id])
end
end
The action happens here
new_game = %Game{
name: attrs.name,
started_at: attrs.started_at
}
user =
Accounts.get_user_with_games(user_id)
|> Ecto.Changeset.change()
|> Ecto.Changeset.put_assoc(:games, [new_game])
|> Repo.update!()
where get_user_with_games is
get_user!(user_id)
|> Repo.preload(:games)
I realise right now I need to include any existing related games but I can get to that later.
I get this
[debug] QUERY OK db=1.0ms idle=895.3ms
begin []
↳ ScrabbleWeb.GameLive.Form.save_game/3, at: lib/scrabble_web/live/game_live/form.ex:87
[debug] QUERY OK source="games" db=1.6ms
INSERT INTO "games" ("name","started_at","inserted_at","updated_at") VALUES ($1,$2,$3,$4) RETURNING "id" ["ABC", ~U[2026-02-12 19:12:43Z], ~U[2026-02-12 19:12:43Z], ~U[2026-02-12 19:12:43Z]]
↳ ScrabbleWeb.GameLive.Form.save_game/3, at: lib/scrabble_web/live/game_live/form.ex:87
[debug] QUERY ERROR source="users_games" db=0.5ms
INSERT INTO "users_games" ("user_id","game_id") VALUES ($1,$2) [1, 6]
↳ ScrabbleWeb.GameLive.Form.save_game/3, at: lib/scrabble_web/live/game_live/form.ex:87
[debug] QUERY OK db=0.2ms
rollback []
↳ ScrabbleWeb.GameLive.Form.save_game/3, at: lib/scrabble_web/live/game_live/form.ex:87
[error] GenServer #PID<0.1178.0> terminating
** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "inserted_at" of relation "users_games" violates not-null constraint
table: users_games
column: inserted_at
Which looks so close…it creates a transaction creates the game but when it is inserting into users_games it omits the timestamps.
What am I missing?
It looks like building the query
INSERT INTO "users_games" ("user_id","game_id") VALUES ($1,$2) [1, 6]
should know there are timestamp fields that could be populated for this.
Should I sack the many to many and create an intermediate entity and and have two one_to_many relationships and do it manually?
Trending in Questions
I’m working on a project that simulates the bumbl example in the programming phoenix book. It acts almost like an email client. We have a...
New
I’m seeing that a list inside a Kino.DataTable will be interpreted as a charlist, even if the Kino.configure() is set to charlists: :as_l...
New
Hello,
I know there is an approach for handling lists that allows for optimized traversal, but I can’t recall the specific method (somet...
New
So my question is quite simple and i have found no conclusive answer on forum, google or AI.
Should we use :erlang.float for Integer to ...
New
Hi, I’ve just set up an application with ash_authentication. There is only magic link strategy for now, so there is no confirmation add o...
New
If a change or preparation module uses Ash.Changeset.get_argument/2 or Ash.Query.get_argument/2 (or any of the other get_argument functio...
New
apply_graft/2 doesn’t rewrite an add_many sub-workflow’s deps on an add step. Grafted jobs cancel with “upstream job was deleted”
Version...
New
Other Trending Topics
I am happy to introduce the very α version of the new programming language compiled to BEAM.
Welcome Cure.
It has literally three kille...
New
Hi there! We created Gust: A task orchestrator inspired by Airflow.
For those who have never heard about Aiflow, it’s a Python-based wor...
New
Beam Bots (or just BB for short) is a framework for building fault-tolerant robotics applications in Elixir using familiar OTP patterns. ...
New
Xamal is a deployment tool for Elixir apps that deploys native releases to bare metal servers over SSH. It’s a port of GitHub - basecamp/...
New
Hello everyone. After busy few months I am happy to announce v0.1.0 of Emerge & Solve.
They are GUI (Emerge) and State management (S...
New
Corex is an accessible, unstyled UI component library for Phoenix that integrates Zag.js state machines using Vanilla JavaScript and Live...
New
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #library
- #deployment
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #elixirconf
- #channels
- #exunit
- #discussion
- #code-sync
- #podcasts
- #javascript
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #ai
- #elixirconf-us
- #blog-post
- #elixir-ls
- #phoenix_html
- #iex
- #graphql
- #genstage
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #hex
- #security
- #metaprogramming










Showing Posts 1 to 9- Show Best Posts
- Show All (oldest first)
- Show All (newest first)
jdiago
Do you need the timestamp on the join table? If not, delete it.
If you do need it, I think doing the many-to-many through a join schema should work. Associations — Ecto v3.14.0
edit: also, this section of the guide talks about the differences between having a join schema and not. Polymorphic associations with many to many — Ecto v3.14.0
linusdm
Since you’re using the
:join_throughoption with a string value, there is nothing more that Ecto knows about this join table. It knows the two columns with the foreign keys (using the_idsuffix). But it knows nothing about your timestamp columns.Read the documentation on
Ecto.Schema.many_to_manyagain, and you’ll see they make a distinction between passing in just a string, and passing in a schema.From the docs:
and:
There are many other ways to model many-to-many relations in Ecto. But if the missing timestamp is your only issue right now, I’d define the schema, and you’re all set.
You could also define a default value (
NOW()) for a inserted_at column in your migration. Then the database handles setting this value for you, instead of Ecto. But you would still need to model a schema for the join table if you need the timestamp in your application somewhere.roganjoshua
This looks precise, logical and excellent.
I missed that bit of the documentation and look forward to trying it tomorrow.
roganjoshua
I did get this working in the end.
I added a schema for the M2M relation
Note: there is an auto primary key (id)
the relations on the other schemas now reference the above schema
for example,
Then to create the relation and the game
It is essential as this is a put to collect the games already belonging to the user and add them to list
[new_game | games].Sub-optimal in terms of performance as the
users_gamessince there is a overhead for the id column and the use of put.It not entirely intuitive and the documentation suggests the intermediate relation schema isn’t necessary.
LostKobrakai
You don’t need that if you configure the schema to not use an
:idcolumn. Once you’re using a schema you can use it to provide all manner of additional information to ecto.Where exactly do you read it like that. Imo the documentation has always been very explicit that no-schema means no additional columns – especially given in the past there was no option for providing a schema at all, so this really needed to be explicit.
roganjoshua
The documentation for Many to Many
Through a join table as an alternative to Through a join schema only include the migration and no schema for the join.
Using a join schema
also sets primary key to be false which didn’t work for me as the query was returning an id which didn’t exist and threw an error.
LostKobrakai
Ok, those cheatsheets are quite a bit newer. That’s something to be fixed then.
You might also need to declare the foreign key columns to be the primary keys, but granted I never actually tried that.
Edit: I setup an issue for fixing the docs: Cheatsheet many-to-many migration incorrect · Issue #4703 · elixir-ecto/ecto · GitHub
roganjoshua
I have update the schema and migration according to your suggestions
and
And this now works without the id column
jdiago
Looks like my attempt at clarifying it got merged. Updates many-to-many guide by jdiago · Pull Request #4704 · elixir-ecto/ecto · GitHub