Perform database constraint operations in changeset

I have a changeset that has check_constraint and foreign_key_constraint but I don’t want to use Repo.insert() immediately. How can I trigger the check_constraint and foreign_key_constraint changesets without using Repo.insert() after? Because changeset.valid? only works for cast and validate_required.

@spec changeset(%User{}, map) :: %Ecto.Changeset{}
  def changeset(user, attrs) do
    |> cast(attrs, [:name, :age, :master_id])
    |> validate_required([:name, :age, :master_id])
    |> check_constraint(:age,
      name: :age_must_be_positive,
    |> foreign_key_constraint(:property_id, message: "master id does not exist")

No Repo.insert() yet

User.changeset(%User{}, %{name: "John", age: 18, master_id: 33})

Is there any ecto function that can trigger the constraint not using Repo.insert()?

I don’t think so, as constraints are checked at db level…

BTW your age>0 constraint could be done by a custom validation.

But how would You solve unique constraints without querying the DB?

Just found a solution. I’ll just use validate_change/3

validate_change(changeset, :master_id, fn :master_id, master_id  ->
  if MyApp.master_exists?(id) do
    [master_id: "master id already exists"]

If this hits the db it‘ll be prone to race conditions. The id might exist when doing the validation, but no longer exist when you do your insert.

Good point.