How to create assoc constraint in Ash?

How to create assoc constraint in ASH, validate before deleting? Like this:

 def check_no_assoc_constraint(author) do
   author    
    |> changeset()
    |> no_assoc_constraint(:post) (has_many)
    |> check_assoc_constraint(author, [:comments]) (many_to_many)
  end

defp check_assoc_constraint(changeset, table, relations) do
        relations
        |> Enum.reduce(changeset, fn relation, changeset ->
          if Ecto.assoc(table, relation) |> Repo.exists?() do
            changeset |> add_error(relation, "Error")
          else
            changeset
          end
        end)
      end

If you’re using postgres, this should happen automatically when you attempt to delete the thing in the data layer. But if you want to do it ahead of time for whatever reason, here is one that you could reuse that supports any kind of relationship in any kind of data layer. There are more optimized methods of doing this, but only by a bit and this one is more generalized. Either way, it should be a decent starting point :slight_smile: I just threw this together, haven’t run the code. Something like this:

defmodule NothingRelated do
  use Ash.Resource.Validation

  def validate(changeset, opts) do
    relationships = Enum.map(opts[:relationships], &Ash.Resource.Info.relationship(changeset.resource, &1))

    load_statement =
      Enum.map(relationships, fn relationship ->
        query = Ash.Query.limit(relationship.destination, 1)
        [{relationship.name, query}]
      end)
    
    loaded = changeset.api.load!(load_statement)
    
    relationships
    # get rid of the ones that are empty
    |> Enum.reject(fn relationship -> 
      Map.get(changeset.data, relationship.name) in [nil, []]
    end)
    |> case do
      [] -> :ok
      remaining ->
        errors = Enum.map(remaining, fn relationship -> "would leave #{relationship.name} behind" end)
        {:error, errors}
   end
  end
end

That you could use in a resource like this:

validations do
  validate {NothingRelated, relationships: [:foo, :bar, :bar]}, on: :destroy
end
1 Like

Tks very much!!