Destroying related resources

I have some related resources with existing actions utilizing manage_relationship to create and update them. For destroying, is it pretty much the same process, where the form has all the nested subforms and then I take those in through arguments and manage_relationship them and destroy them? I haven't seen anything explicitly on this topic in the documentation or here, pardon me if I've missed it. I want to make sure I don't leave any orphaned resources, which the loaded form on my edit page should take care of, right, but I'm wondering if there's a way you're supposed to do it directly in the action to be sure.
8 Replies
kernel
kernel3y ago
if you want to be 100% certain, you can make sure you use Foreign Keys and cascade deletes
axdc
axdcOP3y ago
I'm ~not familiar with how to instrument that from within Ash. The setup I have now is edit.ex
...
def handle_event("destroy", %{}, socket) do
destroy_form =
AshPhoenix.Form.for_destroy(socket.assigns.site, :destroy_site,
api: Panacea.Sites,
forms: [auto?: true],
actor: socket.assigns.current_user
)
|> to_form

case AshPhoenix.Form.submit(destroy_form) do
:ok ->
{:noreply,
socket
|> put_flash(:info, "Site destroyed successfully")
|> push_navigate(to: ~p"/commander/sites")}

{:error, error} ->
IO.inspect(error)

{:noreply,
socket
|> put_flash(:error, "There was a problem destroying this site")
|> push_patch(to: ~p"/commander/sites/#{socket.assigns.site}/edit")}
end
end
...
...
def handle_event("destroy", %{}, socket) do
destroy_form =
AshPhoenix.Form.for_destroy(socket.assigns.site, :destroy_site,
api: Panacea.Sites,
forms: [auto?: true],
actor: socket.assigns.current_user
)
|> to_form

case AshPhoenix.Form.submit(destroy_form) do
:ok ->
{:noreply,
socket
|> put_flash(:info, "Site destroyed successfully")
|> push_navigate(to: ~p"/commander/sites")}

{:error, error} ->
IO.inspect(error)

{:noreply,
socket
|> put_flash(:error, "There was a problem destroying this site")
|> push_patch(to: ~p"/commander/sites/#{socket.assigns.site}/edit")}
end
end
...
site.ex
...
# destroy site and accompanying resources
destroy :destroy_site do
argument :destroy_configuration, :map do
allow_nil? false
end

argument :destroy_domains, {:array, :map} do
allow_nil? false
end

argument :destroy_profiles, {:array, :map} do
allow_nil? false
end

change manage_relationship(:destroy_configuration, :configuration, on_match: :destroy)
change manage_relationship(:destroy_domains, :domains, on_match: :destroy)
change manage_relationship(:destroy_profiles, :profiles, on_match: :destroy)
end
...
...
# destroy site and accompanying resources
destroy :destroy_site do
argument :destroy_configuration, :map do
allow_nil? false
end

argument :destroy_domains, {:array, :map} do
allow_nil? false
end

argument :destroy_profiles, {:array, :map} do
allow_nil? false
end

change manage_relationship(:destroy_configuration, :configuration, on_match: :destroy)
change manage_relationship(:destroy_domains, :domains, on_match: :destroy)
change manage_relationship(:destroy_profiles, :profiles, on_match: :destroy)
end
...
And I'm getting the error:
errors: [
destroy_profiles: {"is required", []},
destroy_domains: {"is required", []},
destroy_configuration: {"is required", []}
],
errors: [
destroy_profiles: {"is required", []},
destroy_domains: {"is required", []},
destroy_configuration: {"is required", []}
],
The form appears to have those subforms in it when I IO.inspect it, so I'm a bit perplexed. Maybe I'm keying it wrong? It mostly matches the setup for my create and edit actions right now, except without an actual form you can edit in the template itself, since we're just destroying.
ZachDaniel
ZachDaniel3y ago
Ah, yeah so this isn't how you want to do this
postgres do
references do
reference, :configuration, on_delete: :delete
reference, :domains, on_delete: :delete
reference, :profiles, on_delete: :delete
end
end
postgres do
references do
reference, :configuration, on_delete: :delete
reference, :domains, on_delete: :delete
reference, :profiles, on_delete: :delete
end
end
Then generate migrations This will tell the database to destroy those things whenever the parent thing is destroyed
axdc
axdcOP3y ago
Okay yeah that's way more modelly and Ash-y. I figured there would be something nice like that. When I drop and recreate the database with migrations and all, it's still telling me the delete can't be performed because it would leave records behind:
%Ash.Error.Invalid{
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :id,
message: "would leave records behind",
private_vars: [
constraint: :foreign,
constraint_name: "profiles_site_id_fkey"
],
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
],
%Ash.Error.Invalid{
errors: [
%Ash.Error.Changes.InvalidAttribute{
field: :id,
message: "would leave records behind",
private_vars: [
constraint: :foreign,
constraint_name: "profiles_site_id_fkey"
],
value: nil,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
],
Specifically calling out Profiles. Is there more I should be aware of when constructing these relationships? I'm reading through everything I can find on references on ashhq now, thank you!!
ZachDaniel
ZachDaniel3y ago
That’s the primary thing, configuring the references let’s you do pretty much everything you should need for this case
axdc
axdcOP3y ago
Okay so it looks like the references are supposed to be defined on each child, not on the parent, so I had that backwards initially. That was my misunderstanding which I corrected by looking through the realworld source here: https://github.com/team-alembic/realworld/search?q=reference I then defined the references block on each of the child resources of the Site (Domains, Configuration, etc) and now they all tidily happily disappear when their Site is destroyed. So I think I get it now. Thank you 🥲
GitHub
Search · reference · team-alembic/realworld
Contribute to team-alembic/realworld development by creating an account on GitHub.
\ ឵឵឵
\ ឵឵឵3y ago
Is there a builtin to accomplish the same at the Ash layer, for the case where not all resources are in Postgres?
ZachDaniel
ZachDaniel3y ago
I don't believe so, no

Did you find this page helpful?