Creating an invalid relationship does not return an error changeset but raises an error

I am trying to test a relationship between a parent and a child.

The migration for the minor is the following one:

defmodule ReservationBook.Repo.Migrations.CreateMinors do
  use Ecto.Migration

  def change do
    create table(:minors) do
      add :name, :string, null: false
      add :surname, :string, null: false
      add :age, :integer, null: false
      add :course, :string, null: false
      add :user_id, references("users", on_delete: :delete_all), null: false


    create index(:minors, [:user_id])

The struct if the following one:

defmodule ReservationBook.Attendees.Minor do
  use Ecto.Schema
  import Ecto.Changeset

  schema "minors" do
    field :age, :integer
    field :course, :string
    field :name, :string
    field :surname, :string
    belongs_to :user, ReservationBook.Accounts.User


  @doc false
  def changeset(minor, attrs) do
    |> cast(attrs, [:name, :surname, :age, :course, :user_id])
    |> validate_required([:name, :surname, :age, :course])
    |> foreign_key_constraint(:user_id)

My problem is the the foreign_key_constraint in the changeset. Even though I am expecting the code to return a changeset {:error, changeset}, what I get is an error in Postgres

   ** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "user_id" violates not-null constraint

         table: minors
         column: user_id

     Failing row contains (538, Sergio, bifvcmpw qtvtos, 11, primary first, null, 2021-01-26 17:53:27, 2021-01-26 17:53:27).
     code: result = Attendees.create_minor(attrs = valid_minor_attributes(%{}, [new_user: false]))
       (ecto_sql 3.5.4) lib/ecto/adapters/sql.ex:751: Ecto.Adapters.SQL.raise_sql_call_error/1
       (ecto 3.5.6) lib/ecto/repo/schema.ex:649: Ecto.Repo.Schema.apply/4
       (ecto 3.5.6) lib/ecto/repo/schema.ex:262: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
       test/reservation_book/attendees_test.exs:39: (test)

I have tried with cast_assoc, and made many changes in the code but I always get the same error instead of the changeset I was expecting

What I am doing wrong?

you need to validate_required :user_id too.

The migration said null: false

add :user_id, references(“users”, on_delete: :delete_all), null: false

All that Ecto.Changeset.foreign_key_constraint does is set some metadata in the changeset that tells Ecto “map an error 23503 into a changeset error if it happens”.

A column covered by a foreign key constraint is still allowed to be null, so that error doesn’t happen. Instead, Postgres returns an error 23502 because the column is defined to not allow null.


I added “user_id” to the validate_required list and it does return the changeset with error that I was expecting. It would be great to specify that in the documentation, as reading the description I thought that it was not necessary (in fact I removed it after reading the docs).

Now I can test that the id is there and that the user is valid

    test "create_minor/1 with no user returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = Attendees.create_minor(attrs = valid_minor_attributes(%{}, [new_user: false]))

    test "create_minor/1 with a wrong user returns error changeset" do
      assert {:error, %Ecto.Changeset{}} = Attendees.create_minor(attrs = valid_minor_attributes(%{user_id: 1}, [new_user: false]))