I have some resources related to each other and I am trying to update them but am getting a "would leave records behind"
error.
Resources
The resources and relationships are as follows.
Reactant
defmodule Flame.App.Reactant do
use Ash.Resource,
data_layer: AshPostgres.DataLayer
Actions
actions do
defaults [:create, :read, :update, :destroy]
update :redefine do
argument :sources, {:array, :map}
argument :formulas, {:array, :map}
change manage_relationship(:sources, :sources,
on_lookup: :relate,
on_match: :ignore,
on_no_match: :create,
on_missing: :unrelate,
use_identities: [:unique_description]
)
change manage_relationship(:formulas, :formulas,
error_path: :formulas,
on_lookup: :ignore,
on_no_match: :create,
on_match: :update,
on_missing: :destroy,
use_identities: [:_primary_key]
)
end
end
Relationships
relationships do
has_many :formulas, Flame.App.Formula do
destination_attribute :owner_id
end
many_to_many :sources, Flame.App.Source do
through Flame.App.ReactantSource
source_attribute_on_join_resource :reactant_id
destination_attribute_on_join_resource :source_id
end
end
end
Formula
defmodule Flame.App.Formula do
use Ash.Resource,
data_layer: AshPostgres.DataLayer
Actions
actions do
defaults([:read])
create :create do
argument :formula, {:array, :map}
primary? true
change manage_relationship(:formula, :reactants_join_assoc,
on_lookup: :ignore,
on_no_match: :create,
on_match: :update,
on_missing: :destroy
)
end
update :update do
argument :formula, {:array, :map}
primary? true
change manage_relationship(:formula, :reactants_join_assoc,
on_lookup: :ignore,
on_no_match: :create,
on_match: :update,
on_missing: :destroy
)
end
destroy :destroy do
argument :formula, {:array, :map}
primary? true
change manage_relationship(:formula, :reactants_join_assoc,
on_lookup: :ignore,
on_no_match: :ignore,
on_match: :destroy,
on_missing: :destroy
)
end
end
Relationships
relationships do
belongs_to :owner, Flame.App.Reactant
many_to_many :reactants, Flame.App.Reactant do
through Flame.App.FormulaReactant
source_attribute_on_join_resource :formula_id
destination_attribute_on_join_resource :reactant_id
end
end
end
FormulaReactant
defmodule Flame.App.FormulaReactant do
use Ash.Resource,
data_layer: AshPostgres.DataLayer
Actions
actions do
defaults [:read, :destroy]
create :create do
argument :reactant, :map do
allow_nil? false
end
argument :unit, :map do
allow_nil? false
end
primary? true
change manage_relationship(:reactant, :reactant,
on_lookup: :relate,
on_no_match: :error,
on_match: :ignore,
on_missing: :unrelate,
use_identities: [:unique_identity]
)
change manage_relationship(:unit, :unit,
on_lookup: :relate,
on_no_match: :error,
on_match: :ignore,
on_missing: :unrelate,
use_identities: [:unique_alias]
)
end
update :update do
argument :reactant, :map do
allow_nil? false
end
validate {Flame.App.Validations.ReactantExists, argument: :reactant}
primary? true
argument :unit, :map do
allow_nil? false
end
change manage_relationship(:reactant, :reactant,
on_lookup: :relate,
on_no_match: :error,
on_match: :ignore,
on_missing: :unrelate,
use_identities: [:unique_identity],
error_path: :reactant
)
change manage_relationship(:unit, :unit,
on_lookup: :relate,
on_no_match: :error,
on_match: :ignore,
on_missing: :unrelate,
use_identities: [:unique_alias],
error_path: :unit
)
end
end
Relationships
relationships do
belongs_to :unit, Flame.App.Unit, allow_nil?: false
belongs_to :formula, Flame.App.Formula, primary_key?: true, allow_nil?: false
belongs_to :reactant, Flame.App.Reactant, primary_key?: true, allow_nil?: false
end
end
With these resources if I get a record and try to update it as follows.
Update Attempt
record
|> Ash.Changeset.for_update(:redefine, %{formulas: []})
|> Flame.App.update()
I am attempting to remove all owned Flame.App.Formula
but I get the following error.
Error
{:error,
%Ash.Error.Invalid{
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :id,
message: "would leave records behind",
private_vars: [
constraint: :foreign,
constraint_name: "formula_reactant_formula_id_fkey"
],
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
],
stacktraces?: true,
changeset: #Ash.Changeset<
api: Flame.App,
action_type: :destroy,
action: :destroy,
attributes: %{},
relationships: %{},
arguments: %{},
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :id,
message: "would leave records behind",
private_vars: [
constraint: :foreign,
constraint_name: "formula_reactant_formula_id_fkey"
],
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
],
data: #Flame.App.Formula<
reactants: #Ash.NotLoaded<:relationship>,
owner: #Ash.NotLoaded<:relationship>,
reactants_join_assoc: #Ash.NotLoaded<:relationship>,
__meta__: #Ecto.Schema.Metadata<:loaded, "formulas">,
id: "481f3e87-300f-4584-bc85-2839f0a9b7c2",
created_at: ~U[2023-10-18 06:09:08.155000Z],
updated_at: ~U[2023-10-18 06:09:08.155000Z],
owner_id: "79cc0070-e6aa-4fbe-b741-1db5a8149cdc",
aggregates: %{},
calculations: %{},
...
>,
context: %{
accessing_from: %{name: :formulas, source: Flame.App.Reactant},
actor: nil,
authorize?: false
},
valid?: false
>,
query: nil,
error_context: [nil],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}}
I’m not sure how to be able to unset the Flame.App.Formula
on a Flame.App.Reactant
.
Notes
- I did not include the full resource specs since I thought they were unrequired
- I am using the
:'relationship'_join_assoc