Terryble
Terryble
AEAsh Elixir
Created by Terryble on 7/13/2023 in #support
many_to_many exception after updating Ash
Previous version was 2.6.31. I updated to 2.11.8 today and an existing many_to_many association that I had is now raising an exception. Did something change? Here are my resources:
defmodule MyApp.Budgeting.Book do
relationships do
many_to_many :users, MyApp.Authentication.User do
through MyApp.Budgeting.BookUser
api MyApp.Authentication
source_attribute_on_join_resource :book_id
destination_attribute_on_join_resource :user_id
end
end
end
defmodule MyApp.Budgeting.Book do
relationships do
many_to_many :users, MyApp.Authentication.User do
through MyApp.Budgeting.BookUser
api MyApp.Authentication
source_attribute_on_join_resource :book_id
destination_attribute_on_join_resource :user_id
end
end
end
defmodule MyApp.Authentication.User do
relationships do
many_to_many :books, MyApp.Budgeting.Book do
through MyApp.Budgeting.BookUser
api MyApp.Budgeting
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :book_id
end
end
end
defmodule MyApp.Authentication.User do
relationships do
many_to_many :books, MyApp.Budgeting.Book do
through MyApp.Budgeting.BookUser
api MyApp.Budgeting
source_attribute_on_join_resource :user_id
destination_attribute_on_join_resource :book_id
end
end
end
defmodule MyApp.Budgeting.BookUser do
relationships do
belongs_to :book, MyApp.Budgeting.Book do
allow_nil? false
end

belongs_to :user, MyApp.Authentication.User do
api MyApp.Authentication
allow_nil? false
end
end
end
defmodule MyApp.Budgeting.BookUser do
relationships do
belongs_to :book, MyApp.Budgeting.Book do
allow_nil? false
end

belongs_to :user, MyApp.Authentication.User do
api MyApp.Authentication
allow_nil? false
end
end
end
Here's the error I'm getting:
** (RuntimeError) Resource `MyApp.Budgeting.BookUser` is not accepted by api `MyApp.Authentication` for autogenerated join relationship: `:users_join_assoc`

Relationship was generated by the `many_to_many` relationship `:users`

If the `through` resource `MyApp.Budgeting.BookUser` is not accepted by the same
api as the destination resource `MyApp.Authentication.User`,
then you must define that relationship manually. To define it manually, add the following to your
relationships:

has_many :users_join_assoc, MyApp.Budgeting.BookUser do
# configure the relationship attributes
...
end

You can use a name other than `:users_join_assoc`, but if you do, make sure to
add that to `:users`, i.e

many_to_many :users_join_assoc, MyApp.Budgeting.BookUser do
...
join_relationship_name :your_new_name
end
** (RuntimeError) Resource `MyApp.Budgeting.BookUser` is not accepted by api `MyApp.Authentication` for autogenerated join relationship: `:users_join_assoc`

Relationship was generated by the `many_to_many` relationship `:users`

If the `through` resource `MyApp.Budgeting.BookUser` is not accepted by the same
api as the destination resource `MyApp.Authentication.User`,
then you must define that relationship manually. To define it manually, add the following to your
relationships:

has_many :users_join_assoc, MyApp.Budgeting.BookUser do
# configure the relationship attributes
...
end

You can use a name other than `:users_join_assoc`, but if you do, make sure to
add that to `:users`, i.e

many_to_many :users_join_assoc, MyApp.Budgeting.BookUser do
...
join_relationship_name :your_new_name
end
8 replies
AEAsh Elixir
Created by Terryble on 4/23/2023 in #support
How to do polymorphic relationships, but with only 1 table?
Apologies if this has been answered before, but I couldn't find it in the search function. I want to create a table called transactions with the following columns:
transactions
------------
source_id
source_type
amount
destination_id
destination_type
transactions
------------
source_id
source_type
amount
destination_id
destination_type
I know there is documentation regarding polymorphic resources in Ash, but it uses multiple tables to create the relationship. My intention with the transactions table is to be able to do various combinations of source_type and destination_type, so I don't think having multiple tables is a good solution. How can I do this in Ash?
7 replies
AEAsh Elixir
Created by Terryble on 4/13/2023 in #support
How to filter associations when preloading them?
I could probably do this with Ecto.Query instead, but I was wondering how I would do it with Ash.Query The scenario, I want to list out all of the Post and I also want to preload the Category association of each post. I also want to filter the categories to only ones that contain a specific category_id.
4 replies
AEAsh Elixir
Created by Terryble on 4/4/2023 in #support
How to test LiveView behind AshAuthentication?
I have some LiveViews that require an authenticated user to access (AshAuthentication). How do I simulate login for the user? I saw that there are a few tests in the AshHQ repo and I did try to copy all of the relevant stuff, but I still can't get it to work. Here's what I have so far:
defmodule MyAppWeb.BookLiveTest do
use MyAppWeb.ConnCase

import Phoenix.LiveViewTest

describe "with existing books" do
setup %{conn: conn} do
result = register_and_log_in_user(%{conn: conn})
book = book_fixture(result[:user], %{name: "Test Book"})

%{
conn: result[:conn],
user: result[:user],
book: book
}
end

test "lists all books", %{conn: conn, book: book} do
{:ok, _index_live, html} = live(conn, ~p"/books")

assert html =~ "Listing Books"
assert html =~ book.name
end
end
end
defmodule MyAppWeb.BookLiveTest do
use MyAppWeb.ConnCase

import Phoenix.LiveViewTest

describe "with existing books" do
setup %{conn: conn} do
result = register_and_log_in_user(%{conn: conn})
book = book_fixture(result[:user], %{name: "Test Book"})

%{
conn: result[:conn],
user: result[:user],
book: book
}
end

test "lists all books", %{conn: conn, book: book} do
{:ok, _index_live, html} = live(conn, ~p"/books")

assert html =~ "Listing Books"
assert html =~ book.name
end
end
end
defmodule MyAppWeb.LoginTestHelpers do
alias MyApp.AccountsFixtures

def register_and_log_in_user(%{conn: conn}) do
user = AccountsFixtures.user_fixture()

%{conn: log_in_user(conn, user), user: user}
end

def register_user(_context) do
%{
user: AccountsFixtures.user_fixture()
}
end

def log_in_user(conn, user) do
conn
|> Phoenix.ConnTest.init_test_session(%{})
|> Plug.Conn.put_session(:user_token, user.__metadata__.token)
end
end
defmodule MyAppWeb.LoginTestHelpers do
alias MyApp.AccountsFixtures

def register_and_log_in_user(%{conn: conn}) do
user = AccountsFixtures.user_fixture()

%{conn: log_in_user(conn, user), user: user}
end

def register_user(_context) do
%{
user: AccountsFixtures.user_fixture()
}
end

def log_in_user(conn, user) do
conn
|> Phoenix.ConnTest.init_test_session(%{})
|> Plug.Conn.put_session(:user_token, user.__metadata__.token)
end
end
4 replies
AEAsh Elixir
Created by Terryble on 3/7/2023 in #support
How to use AshPhoenix.Form with the default core_components file that comes with Phoenix 1.7?
Phoenix 1.7 has updated the core_components file that comes with mix phx.new which means we need to change the way we use AshPhoenix.Form to play nicely with the updated form components. From what I gathered in some of the previous conversations, the way to do it in the new component should be as follows. When calling <.simple_form />, the :let should removed so it would look like this:
<.simple_form
for={@form}
id="post-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save">
<.simple_form
for={@form}
id="post-form"
phx-target={@myself}
phx-change="validate"
phx-submit="save">
When it comes to using <.input />, the field attribute should look like this:
<.input field={@form[:title]} type="text" label="Title" />
<.input field={@form[:title]} type="text" label="Title" />
When defining the form, we can still use the regular AshPhoenix.Form.for_action, AshPhoenix.Form.for_create, and AshPhoenix.Form.for_update and then calling to_form() to make it compliant with the new components. When calling the validate handle_event, there are no issues with just assigning AshPhoenix.Form.validate() |> to_form() for the form assigns in the socket. My problem is with calling AshPhoenix.Form.submit. I am getting an error when doing the following:
AshPhoenix.Form.submit(socket.assigns.form, params: post_params)
AshPhoenix.Form.submit(socket.assigns.form, params: post_params)
I am getting the following error:
** (Protocol.UndefinedError) protocol Phoenix.HTML.FormData not implemented for {:ok, #MyApp.Blog.Post<..>} of type Tuple. This protocol is implemented for the following type(s): AshPhoenix.FilterForm, AshPhoenix.FilterForm.Arguments, AshPhoenix.FilterForm.Predicate, AshPhoenix.Form, Atom, Ecto.Changeset, Map, Plug.Conn
** (Protocol.UndefinedError) protocol Phoenix.HTML.FormData not implemented for {:ok, #MyApp.Blog.Post<..>} of type Tuple. This protocol is implemented for the following type(s): AshPhoenix.FilterForm, AshPhoenix.FilterForm.Arguments, AshPhoenix.FilterForm.Predicate, AshPhoenix.Form, Atom, Ecto.Changeset, Map, Plug.Conn
What should I do to fix this? For some reason, it actually persists in the database. It's just that I'm getting this error if I call the line above. I am using ash_phoenix 1.2.8
7 replies
AEAsh Elixir
Created by Terryble on 3/2/2023 in #support
How to set a default value for AshPhoenix.Form.for_action ?
I have a Resource that has a foreign key in it which I assign as a hidden field in my form. How do I make it so that I can fill out the foreign key field when calling AshPhoenix.Form.for_action or AshPhoenix.Form.for_create?
7 replies
AEAsh Elixir
Created by Terryble on 2/28/2023 in #support
How to do the LiveComponent preload example in Ash.Query?
In the docs for the Phoenix.LiveComponent (https://hexdocs.pm/phoenix_live_view/Phoenix.LiveComponent.html#module-preloading-and-update) there is a section there that shows how to use preload/1 to solve the N+1 problem:
def preload(list_of_assigns) do
list_of_ids = Enum.map(list_of_assigns, & &1.id)

users =
from(u in User, where: u.id in ^list_of_ids, select: {u.id, u})
|> Repo.all()
|> Map.new()

Enum.map(list_of_assigns, fn assigns ->
Map.put(assigns, :user, users[assigns.id])
end)
end
def preload(list_of_assigns) do
list_of_ids = Enum.map(list_of_assigns, & &1.id)

users =
from(u in User, where: u.id in ^list_of_ids, select: {u.id, u})
|> Repo.all()
|> Map.new()

Enum.map(list_of_assigns, fn assigns ->
Map.put(assigns, :user, users[assigns.id])
end)
end
How would I do the query in this section in Ash.Query?
6 replies
AEAsh Elixir
Created by Terryble on 2/26/2023 in #support
AshPhoenix.Form for an Ash.Flow module instead of Ash.Resource
I really like AshPhoenix and I just figured out how to use Ash.Flow. Is it possible to initialize an AshPhoenix.Form object for an Ash.Flow module instead of the usual Ash.Resource?
6 replies
AEAsh Elixir
Created by Terryble on 2/24/2023 in #support
Where and how do I write my business logic?
I know this is a silly question and I apologize as I'm still new to Ash, but where exactly do I write my business logic? I have a Book and Category resource. What I want to happen is to create a bunch of Category records every time a Book is created. Normally, I would write this business logic in a context/module somewhere and then call it inside the save handle_event in my liveview after a successful Book record creation. I honestly can't figure out if I should just do the same thing - write a module somewhere in the app and call it once AshPhoenix.Form.submit() succeeds. Or maybe there is a convention or an actual methodology when doing these things with Ash and AshPhoenix?
14 replies
AEAsh Elixir
Created by Terryble on 2/22/2023 in #support
How to define a read method for preloading associations inside a resource?
Apologies for the noob question, but I couldn't find it in the docs. Let's say I have an author resource that has_many posts. How do I use the read DSL to get an author by its ID and preload its associated posts?
5 replies