In my previous post, I documented how “has many through” relationships weren’t natively supported in Ash framework, but could be implemented by using no_attributes? true. This option removes the need to have attributes matching to link two records, and instead uses a filter to determine which records are related to which, like this:
defmodule Example.World.Country do
use Ash.Resource, # ...
relationships do
has_many :regions, Example.World.Region
has_many :cities, Example.World.City do
no_attributes? true
filter expr(region.country_id == parent(id))
end
end
endThis lets you load cities that belongs to all regions of a specific country, like so:
iex(1)> Ash.get!(Example.World.Country, %{name: "Australia"}, load: [:cities])
%Example.World.Country{
name: "Australia",
cities: [
%Example.World.City{name: "Melbourne", ...},
...
]
}However, as of Ash v3.24.0, these kinds of relationships are now natively supported! We can simplify the code in the Country resource, to use the new through option on has_many:
defmodule Example.World.Country do
use Ash.Resource, # ...
relationships do
has_many :regions, Example.World.Region
has_many :cities, Example.World.City do
no_attributes? true
filter expr(region.country_id == parent(id))
end
has_many :cities, Example.World.City, through: [:regions, :cities]
end
endThis works exactly the same way as the previous example, but is shorter and much easier to reason about!
You can still add filters and other options to restrict the list of returned records, eg.
has_many :largest_cities, Example.World.City do
# this could also be `through [:cities]` to reuse the existing direct relationship
through [:regions, :cities]
sort population: :desc
limit 3
endThank you to Kenneth Kostrešević for implementing this feature! ❤️