Setting an attribute to a calculated value based on a relation

Hi,

I want to set an attribute based on a relation.
In the concrete example:

    create :create do
      primary? true
      accept [:content, :task_id]
      change relate_actor(:user)

      change set_attribute(:workgroup_id, <<THE WORKGROUP THE USER BELONGS TO>>)
    end

I would like not to have to pass workgroup_id as an argument since it can be derived from the user/actor + task_id. A user can be in many workgroups in an organization, and the task is associated with exactly one organization.

I need to store the workgroup_id, since in the system a user can change organizations and it is important that we know which org the item was created for, we cannot rely on looking it up through the user after the point of creation.

In this case, you’d do this with a custom change.

create :create do
  primary? true
  accept [:content, :task_id]
  change relate_actor(:user)

  change fn changeset, context -> 
    Ash.Changeset.before_action(changeset, fn changeset -> 
      actor = context.actor
      task = Ash.Changeset.get_attribute(changeset, :task_id)
      workgroup_id = # look up the workgroup id
      Ash.Changeset.force_change_attribute(changeset, :workgroup_id, workgroup_id)
    end
  end
end

A small note, I’ve shown the inline anonymous function syntax here for brevity, but the best practice is to define a module to contain this logic.

change YourChange

defmodule YourChange do
  use Ash.Resource.Change

  def change(changeset, _opts, context) do
      actor = context.actor
      task = Ash.Changeset.get_attribute(changeset, :task_id)
      workgroup_id = # look up the workgroup id
      Ash.Changeset.force_change_attribute(changeset, :workgroup_id, workgroup_id)
  end
end