kamaroly
How to Extract a Relationship into a Separate Module for Reuse in `Ash`?
I’m working on a feature that lets users add custom fields to records. For this, I have three resources:
FieldTypewith columnsnameanddescription.Fieldwith columnsname,field_type_id,description, andoptions.FieldResourceRecordwith columnsfield_id,resource_name,resource_record_id, andvalue.
Any resource needing custom fields will have a one-to-many relationship with FieldResourceRecord where resource_name matches ResourceWithManyFields.
Currently, I’m defining the has_many :fields, MyApp.Fields.FieldResourceRecord relationship on each resource. Is there a way to centralize this relationship, similar to how we handle preparations, changes, and manual_actions?
Marked As Solved
kamaroly
It was not working. I fixed it like below:
defmodule Hr.Extensions.CustomFields.Transformers.AddCustomFields do
use Spark.Dsl.Transformer
import Ash.Expr
@doc """
Automatically add relationship to the FieldResourceRecord on any given resource
"""
def transform(dsl) do
Ash.Resource.Builder.add_new_relationship(
dsl,
:has_many,
:fields,
Hr.Fields.FieldResourceRecord,
destination_attribute: :resource_record_id,
filters: expr(resource_name == get_resource_name(dsl))
)
end
defp get_resource_name(dsl) do
dsl.persist.module
|> Atom.to_string()
|> String.replace("Elixir.", "")
end
end
Also the error message says it requires filter options even if it has been passed. So, I figured out that the issues is in the error messages.
The error message should report missing destination_attribute instead of destination_field, and it should report missing filters instead of filter.
Error showing missing filter instead of filters
Error showing destination field missing nstead of destination_attribute
Also Liked
zachdaniel
You can centralize it by writing an extension, although for just one relationship it may be sort of a nuclear option ![]()
defmodule YourApp.CustomFields do
use Spark.Dsl.Extension,
transformers: [YourApp.CustomFields.Transformers.AddCustomFields]
end
defmodule YourApp.CustomFields.Transformers.AddCustomFields do
use Spark.Dsl.Transformer
def transform(dsl) do
dsl
|> Ash.Resource.Builder.add_new_relationship(:has_many, :fields, MyApp>Fields.FieldResourceRecord)
end
end
Then you can do
use Ash.Resource, extensions: [YourApp.CustomFields]
zachdaniel
Make sure to import Ash.Expr
import Ash.Expr
and then you can use it like so:
Ash.Resource.Builder.add_new_relationship(
dsl,
:has_many,
:fields,
Hr.Fields.FieldResourceRecord,
destination_attribute: :resource_record_id,
filter: expr(resource_name == "Hr.People.Person")
)
EDIT: I’m pretty sure that the filter option should “just work” like that, but let me know if not.
Popular in Questions
Other popular topics
Categories:
Sub Categories:
Forums
Popular Tags
- #ecto
- #liveview
- #troubleshooting
- #learning-elixir
- #deployment
- #library
- #erlang
- #testing
- #genserver
- #mix
- #absinthe
- #remote-other
- #otp
- #plug
- #how-to-question
- #macros
- #postgres
- #channels
- #elixirconf
- #exunit
- #discussion
- #code-sync
- #javascript
- #podcasts
- #onsite
- #dialyzer
- #docker
- #authentication
- #umbrella
- #full-time-contract
- #podcasts-by-brainlid
- #ecto-query
- #elixir-ls
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance











