If I have some fields in one of my database tables that can’t simultaneously have the same values, I would create an index and then put the unique_constraint in my changeset, right?
Lets say I have a table of people
with fields first_name
and last_name
. Two people can have the same first name or the same last name, but not both, otherwise they’d be the same person (I know that is not how real life works, it is just an example to make my question less abstract).
I made a migration with create unique_index(:people, [:first_name, :last_name])
to create the index in database level.
Then in my changeset pipeline, I put unique_constraint([:first_name, :last_name])
.
If now I try to add “Lucas Perez” twice, the second insert would return a changeset like:
action: :insert,
changes: %{first_name: "Lucas", last_name: "Perez"},
errors: [
first_name: {"has already been taken", [constraint: :unique, constraint_name: "people_first_name_last_name_index"]}
],
valid?: false
And if I check the constraints of my changeset (changeset.constraints
), I’d have something like this:
[
%{
constraint: "people_first_name_last_name_index",
error_message: "has already been taken",
...
}
]
So far so good, this is exactly what is documented here, no surprises.
But is it possible to have both first_name
and last_name
in the fields of my constraints? So that the errors
list would have both fist_name
and last_name
?
I even tried to call the unique_constraint
twice in my changeset pipeline, but it ends using the last one.
changeset
|> unique_constraint([:first_name, :last_name], name: :people_first_name_last_name_index)
|> unique_constraint([:last_name, :first_name], name: :people_first_name_last_name_index)
If I do this, the changeset.constraints
will have both constraints, each with a different field, but after attempting to insert the same person twice, the changeset.errors
list will only have the last_name
field.
I thought about creating a custom validation, but it is not very good, since the indexes/constraints are only checked at the moment of the database modification, which protects against concurrency. Also, a custom validation would traverse the whole table everytime I create a changeset, and not just at the moment of the database modification.
Is it possible to achieve this? Thanks a lot in advance!