manage_relationship to create another resource `belongs_to` relation

Hi, I noticed I have a pretty common thing to define an action on a resource, which will automatically created related resources that is in the scope of the action. I wanted to know how to do that declaratively Example.
Organization (has_many Member)
Member (belongs_to Organization, belongs_to User)
User (has_many Member)
Organization (has_many Member)
Member (belongs_to Organization, belongs_to User)
User (has_many Member)
I want to define a :create on Organization, that will create a member with actor Currently I am doing it in a custom function that calls
change fn changeset, context ->
Ash.Changeset.manage_relationship(
changeset,
:members,
%{user_id: context.actor.id, role: "owner"},
type: :create
)
end
change fn changeset, context ->
Ash.Changeset.manage_relationship(
changeset,
:members,
%{user_id: context.actor.id, role: "owner"},
type: :create
)
end
But I don't like this way, as there is a manage_relationship Resource action, but I don't want anything passed as arguments
Solution:
manage_relationship will eventually call a create/update action etc., and you are providing inputs for it here
Jump to solution
6 Replies
ZachDaniel
ZachDaniel4mo ago
That's the only way currently.
Vahagn
VahagnOP4mo ago
do you plan on having something like set_attribute, like set_relation? or make a duplicate of manage_relationship, which does not expect an argument, but a value?
ZachDaniel
ZachDaniel4mo ago
No current plans, since often when you do this it's computed state for the relationship in some way There isn't anything wrong with having custom changes like that You can clean it up by putting it into a module and making it generic if you like i.e
defmodule AddMember do
def change(changeset, opts, context) do
Ash.Changeset.manage_relationship(
changeset,
:members,
%{user_id: context.actor.id, role: opts[:role]},
type: :create
)
end
end

change {AddMember, role: "owner"}
defmodule AddMember do
def change(changeset, opts, context) do
Ash.Changeset.manage_relationship(
changeset,
:members,
%{user_id: context.actor.id, role: opts[:role]},
type: :create
)
end
end

change {AddMember, role: "owner"}
If you want it to visually look the same, you can do this
def add_member(role) do
{__MODULE__, role: role}
end
def add_member(role) do
{__MODULE__, role: role}
end
and then in your resource
import AddMember

...

change add_member("owner")
import AddMember

...

change add_member("owner")
Vahagn
VahagnOP4mo ago
Can I pass a Changeset to manage_relationship? something like
member = Member |> Ash.Changeset.for_create(:create)
Ash.Changeset.manage_relationship(changeset, :members, member, type: :create)
member = Member |> Ash.Changeset.for_create(:create)
Ash.Changeset.manage_relationship(changeset, :members, member, type: :create)
so I can define the default role for :create to be "owner"
ZachDaniel
ZachDaniel4mo ago
No, you can only pass action inputs to the underlying action
Solution
ZachDaniel
ZachDaniel4mo ago
manage_relationship will eventually call a create/update action etc., and you are providing inputs for it here

Did you find this page helpful?