How to use same many to many relantionships in multiple domains`

I have an resource User and Organization in my Accounts domain.

In User I have the following many_to_many relationship:

    many_to_many :referred_organizations, Organization do
      through OrganizationReferredUser

      public? true

      source_attribute_on_join_resource :referred_user_id
      destination_attribute_on_join_resource :organization_id
    end

Here is my OrganizationReferredUser implementation:

defmodule Core.Marketplace.Accounts.OrganizationReferredUser do
  @moduledoc false

  alias Core.Marketplace.Accounts

  use Ash.Resource,
    domain: Core.Marketplace.Accounts,
    data_layer: AshPostgres.DataLayer

  code_interface do
    define :read
    define :create
  end

  attributes do
    uuid_v7_primary_key :id

    timestamps()
  end

  relationships do
    alias Accounts.{Organization, User}

    belongs_to :referred_user, User
    belongs_to :organization, Organization
  end

  postgres do
    table "organization_referred_users"

    references do
      reference :referred_user, on_update: :update, on_delete: :delete
      reference :organization, on_update: :update, on_delete: :delete
    end

    repo Core.Repo
  end

  identities do
    identity :unique_association, [:referred_user_id, :organization_id]
  end

  actions do
    defaults [:read, :destroy, :create]
  end
end

Now I want to reuse the same relationship in another domain called Markets, but instead of having the same User resource there, I have one called Offeror instead which uses the same table.

When I try to create the OrganizationReferredOfferor in my Markets domain, the code will not compile in the belongs_to block belongs_to :referred_offeror, Offeror because there will be no referred_offeror_id in my resource and I can’t find an option to tell Ash to actually use the existing (exists in the DB and in the original OrganizationReferredUser resource, not in this resource) referred_user_id instead.

How can I achieve that?

You should be able to set the source_attribute option:

belongs_to ... do
  source_attribute :referred_user_id
end

I tried that, but then I get this error:

** (EXIT from #PID<0.95.0>) an exception was raised:
    ** (Spark.Error.DslError) relationships -> referred_organizations_join_assoc:
  Relationship `referred_organizations_join_assoc` expects destination field `offeror_id` to be defined on Core.Marketplace.Markets.OrganizationReferredOfferor
        (elixir 1.18.1) lib/process.ex:886: Process.info/2
        (spark 2.2.43) lib/spark/error/dsl_error.ex:30: Spark.Error.DslError.exception/1
        (ash 3.4.60) lib/ash/resource/verifiers/validate_relationship_attributes.ex:62: Ash.Resource.Verifiers.ValidateRelationshipAttributes.validate_relationship/2
        (elixir 1.18.1) lib/enum.ex:987: Enum."-each/2-lists^foreach/1-0-"/2
        lib/core/marketplace/markets/offeror.ex:1: anonymous fn/1 in Core.Marketplace.Markets.Offeror.__verify_spark_dsl__/1
        (elixir 1.18.1) lib/enum.ex:4438: Enum.flat_map_list/2
        lib/core/marketplace/markets/offeror.ex:1: Core.Marketplace.Markets.Offeror.__verify_spark_dsl__/1
        (elixir 1.18.1) lib/enum.ex:987: Enum."-each/2-lists^foreach/1-0-"/2

Sorry, I might have missed exactly which thing was wrong :laughing:

Can you share all of the relationships involved as you currently have them? Ultimately you can configure both the source_attribute and destination_attribute of any relationship, so you can 100% do what you’re trying to do.

Found the issue, I needed to also explicitly say what was the field in the offeror resource using source_attribute_on_join_resource :referred_user_id, now it is working great

1 Like