Issue with Foreign Key Constraint in Ecto Changeset
Hello everyone,
I’m encountering an issue with foreign_key_constraint in an Ecto changeset. Specifically, when I attempt to insert a record into my taxas table, I receive the following error:
[error] ** (Ecto.ConstraintError) constraint error when attempting to insert struct:
* nil (foreign_key_constraint)
If you would like to stop this constraint violation from raising an
exception and instead add it as an error to your changeset, please
call `foreign_key_constraint/3` on your changeset with the constraint
`:name` as an option.
The changeset defined the following constraints:
* "taxas_recinto_id_fkey" (foreign_key_constraint)
* "taxas_servico_id_fkey" (foreign_key_constraint)
(ecto 3.12.5) lib/ecto/repo/schema.ex:881: anonymous fn/4 in Ecto.Repo.Schema.constraints_to_errors/3
(elixir 1.17.3) lib/enum.ex:1703: Enum."-map/2-lists^map/1-1-"/2
(ecto 3.12.5) lib/ecto/repo/schema.ex:865: Ecto.Repo.Schema.constraints_to_errors/3
...
Problem Context
I have the following schema for taxas, and migrations:
defmodule Backend.Taxas.Taxa do
use Ecto.Schema
import Ecto.Changeset
alias Backend.Recintos.Recinto
alias Backend.Servicos.Servico
alias Backend.Taxas.Valores
@derive {Jason.Encoder, [except: [:__meta__, :recinto, :servico]]}
schema "taxas" do
belongs_to :recinto, Recinto
belongs_to :servico, Servico
field :tipo, Ecto.Enum, values: [:publica, :negociada]
embeds_one :valores, Valores
timestamps(type: :utc_datetime)
end
@doc false
def changeset(taxa, attrs) do
taxa
|> cast(attrs, [:servico_id, :recinto_id, :tipo])
|> validate_required([:servico_id, :recinto_id, :tipo])
|> cast_embed(:valores, [required: true]) # Fix here for embedding
|> foreign_key_constraint(:servico_id, message: "Servico not found")
|> foreign_key_constraint(:recinto_id, message: "Recinto not found")
end
end
defmodule Backend.Repo.Migrations.CreateTaxas do
use Ecto.Migration
def change do
create table(:taxas) do
add :recinto_id, references(:recintos)
add :servico_id, references(:servicos)
add :tipo, :string
add :valores, :map
timestamps(type: :utc_datetime)
end
end
end
defmodule Backend.Repo.Migrations.CreateServicos do
use Ecto.Migration
def change do
create table(:servicos) do
add :recinto_id, references(:recintos)
add :nome, :string
add :obrigatorio, :boolean, default: false, null: false
add :hasPeriodo, :boolean, default: false, null: false
add :cobranca, :string
add :aplicaTaxaMercadoria, :boolean, default: false, null: false
add :aplicaTaxaContainer, :boolean, default: false, null: false
add :valor, :float
timestamps(type: :utc_datetime)
end
end
end
defmodule Backend.Repo.Migrations.CreateRecintos do
use Ecto.Migration
def change do
create table(:recintos) do
add :recintoId, :integer
add :nome, :string
timestamps(type: :utc_datetime)
end
end
end
In this setup, I have two foreign keys: servico_id and recinto_id. The foreign_key_constraint for these fields is supposed to handle constraint violations when the foreign keys don’t exist in the database.
What I’m Trying to Achieve
I want to catch foreign key violations and add them as errors to the changeset rather than having Ecto raise an exception.
When i insert then using previously added recinto and servico, the api returns successfully.
My question is why do i have that ‘nil’ in where the constraint name were supposed to be.