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
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