mbrg
Using a `no_attributes` relationship in a calculation
Hi,
I have recently finished the Ash Framework book ,and would like to add some more features to my Tunez app, starting with a friend system.
What I came up with was to model everything with a FriendRequest resource between a source and target user, like in this blog post. A user has (accepted) friend requests, a user Alice is friends with Bob if Alice’s ID appears in Bob’s list of accepted friend requests as either the target or the source.
So far I have been able to set up friendships between users and to list their friends. What I want to do now is to add an is_my_friend calculation which works similarly to the followed_by_me calculation on Artist.
My resources look like this (just the relevant bits, the Tunez resource also has account registration etc.):
defmodule FriendshipTest.Accounts.User do
# ... snip
attributes do
uuid_primary_key :id
attribute :username, :ci_string do
allow_nil? false
public? true
end
end
relationships do
has_many :friends, FriendshipTest.Accounts.User do
no_attributes? true
public? true
filter expr(
parent(accepted_friend_requests.target_user_id) in [id, parent(id)] &&
parent(accepted_friend_requests.source_user_id) in [id, parent(id)]
)
end
has_many :accepted_friend_requests, FriendshipTest.Accounts.FriendRequest do
public? true
no_attributes? true
filter expr(
parent(id) in [source_user_id, target_user_id] &&
not pending
)
end
end
calculations do
calculate :is_my_friend, :boolean, expr(exists(friends, id == ^actor(:id))) do
public? true
end
end
# ... snip
end
defmodule FriendshipTest.Accounts.FriendRequest do
# ... snip
attributes do
create_timestamp :inserted_at
attribute :accepted_at, :utc_datetime_usec do
public? true
end
end
relationships do
belongs_to :source_user, FriendshipTest.Accounts.User do
public? true
allow_nil? false
primary_key? true
end
belongs_to :target_user, FriendshipTest.Accounts.User do
public? true
allow_nil? false
primary_key? true
end
end
calculations do
calculate :pending, :boolean, expr(is_nil(accepted_at))
end
# ...snip
end
This works for befriending users and listing their friends:
iex(1)> [user_a, user_b] = Friendshiptest.Repo.all(FriendshipTest.Accounts.User)
iex(2)> FriendshipTest.Accounts.befriend_user!(user_a, actor: user_b)
iex(3)> FriendshipTest.Accounts.befriend_user!(user_b, actor: user_a)
iex(4)> Ash.load!(user_a, :friends)
# ... database query
%FriendshipTest.Accounts.User{
id: "48a91eeb-72ab-4161-af63-68742739c58f",
username: #Ash.CiString<"user_a">,
is_my_friend: #Ash.NotLoaded<:calculation, field: :is_my_friend>,
friends: [
%FriendshipTest.Accounts.User{
id: "8a5a1841-9c3d-4195-8877-f64c0826f7c7",
username: #Ash.CiString<"user_b">,
is_my_friend: #Ash.NotLoaded<:calculation, field: :is_my_friend>,
friends: #Ash.NotLoaded<:relationship, field: :friends>,
accepted_friend_requests: #Ash.NotLoaded<:relationship, field: :accepted_friend_requests>,
__meta__: #Ecto.Schema.Metadata<:loaded, "public", "users">
}
],
accepted_friend_requests: #Ash.NotLoaded<:relationship, field: :accepted_friend_requests>,
__meta__: #Ecto.Schema.Metadata<:loaded, "users">
}
iex(5)> Ash.load!(user_b, :friends)
#...
%FriendshipTest.Accounts.User{
id: "8a5a1841-9c3d-4195-8877-f64c0826f7c7",
username: #Ash.CiString<"user_b">,
is_my_friend: #Ash.NotLoaded<:calculation, field: :is_my_friend>,
friends: [
%FriendshipTest.Accounts.User{
id: "48a91eeb-72ab-4161-af63-68742739c58f",
username: #Ash.CiString<"user_a">,
is_my_friend: #Ash.NotLoaded<:calculation, field: :is_my_friend>,
friends: #Ash.NotLoaded<:relationship, field: :friends>,
accepted_friend_requests: #Ash.NotLoaded<:relationship, field: :accepted_friend_requests>,
__meta__: #Ecto.Schema.Metadata<:loaded, "public", "users">
}
],
accepted_friend_requests: #Ash.NotLoaded<:relationship, field: :accepted_friend_requests>,
__meta__: #Ecto.Schema.Metadata<:loaded, "users">
}
But when I try to load is_my_friend I get this error:
iex(6)> Ash.load!(user_a, :is_my_friend, actor: user_b)
** (Ash.Error.Unknown)
Bread Crumbs:
> Exception raised in: FriendshipTest.Accounts.User.read
Unknown Error
* ** (RuntimeError) Error while building parent reference: accepted_friend_requests.target_user_id
Query so far:
#Ecto.Query<from u0 in FriendshipTest.Accounts.User, as: 1, select: struct(u0, [:id, :username])>
Current bindings:
%{0 => %{type: :root, path: [], source: FriendshipTest.Accounts.User}}
(ash_sql 0.2.86) lib/expr.ex:3302: AshSql.Expr.reference_error!/2
(ash_sql 0.2.86) lib/expr.ex:2283: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1851: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1201: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1851: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1201: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/filter.ex:38: anonymous fn/2 in AshSql.Filter.add_filter_expression/2
(elixir 1.18.4) lib/enum.ex:2546: Enum."-reduce/3-lists^foldl/2-0-"/3
(ash_sql 0.2.86) lib/expr.ex:3302: AshSql.Expr.reference_error!/2
(ash_sql 0.2.86) lib/expr.ex:2283: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1851: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1201: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1851: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/expr.ex:1201: AshSql.Expr.default_dynamic_expr/6
(ash_sql 0.2.86) lib/filter.ex:38: anonymous fn/2 in AshSql.Filter.add_filter_expression/2
iex:6: (file)
Am I missing something obvious?
I have also put the code on GitHub, along with a failing testcase if that helps.
Most Liked
mbrg
https://github.com/ash-project/ash/issues/2214
Thanks for the quick reply, lease let me know if I need to change anything.
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








