AE
Ash Elixirβ€’3y ago
miguels

How to create has_one relationships in a nested form

I have a resource called Transaction that has a has_one relationship with a resource called RecurrencePattern. When creating a new transaction (usually through a form in LV) I want to also create a matching recurrence_pattern. This works great when using the create action directly, but I'm getting an error when using an AshPhoenix.Form (I believe I got it to work at some point but don't know how to get back there again πŸ˜… ) This works:
Transaction.create!(%{
amount: 100,
description: "Test transaction",
date: Date.utc_today(),
category_id: context.category.id,
currency_id: context.currency.id,
method_id: context.method.id,
rate_id: context.rate.id,
type_id: context.type.id,
end_date: Timex.shift(Date.utc_today(), months: 1),
recurrence_pattern: %{
recurrence_type_id: context.recurrence_type.id,
count: 5
}
})
Transaction.create!(%{
amount: 100,
description: "Test transaction",
date: Date.utc_today(),
category_id: context.category.id,
currency_id: context.currency.id,
method_id: context.method.id,
rate_id: context.rate.id,
type_id: context.type.id,
end_date: Timex.shift(Date.utc_today(), months: 1),
recurrence_pattern: %{
recurrence_type_id: context.recurrence_type.id,
count: 5
}
})
This gives me an error:
params = %{
amount: 100,
description: "Test transaction",
date: Date.utc_today(),
category_id: context.category.id,
currency_id: context.currency.id,
method_id: context.method.id,
rate_id: context.rate.id,
type_id: context.type.id,
end_date: Timex.shift(Date.utc_today(), months: 1),
recurrence_pattern: %{
recurrence_type_id: context.recurrence_type.id,
count: 5
}
}

transaction =
AshPhoenix.Form.for_create(Transaction, :create,
api: Transactions,
forms: [auto?: true]
)
|> AshPhoenix.Form.add_form(:recurrence_pattern)
|> AshPhoenix.Form.validate(params)
|> AshPhoenix.Form.submit!()
params = %{
amount: 100,
description: "Test transaction",
date: Date.utc_today(),
category_id: context.category.id,
currency_id: context.currency.id,
method_id: context.method.id,
rate_id: context.rate.id,
type_id: context.type.id,
end_date: Timex.shift(Date.utc_today(), months: 1),
recurrence_pattern: %{
recurrence_type_id: context.recurrence_type.id,
count: 5
}
}

transaction =
AshPhoenix.Form.for_create(Transaction, :create,
api: Transactions,
forms: [auto?: true]
)
|> AshPhoenix.Form.add_form(:recurrence_pattern)
|> AshPhoenix.Form.validate(params)
|> AshPhoenix.Form.submit!()
The error inside the changeset:
%Ash.Error.Changes.Required{
field: :transaction_id,
type: :attribute,
resource: RecurrencePattern,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
%Ash.Error.Changes.Required{
field: :transaction_id,
type: :attribute,
resource: RecurrencePattern,
changeset: nil,
query: nil,
error_context: [],
vars: [],
path: [],
stacktrace: #Stacktrace<>,
class: :invalid
}
23 Replies
miguels
miguelsOPβ€’3y ago
This is the action
create :create do
primary? true

argument :recurrence_pattern, :map

change relate_actor(:user)
change manage_relationship(:recurrence_pattern, type: :direct_control)
end
create :create do
primary? true

argument :recurrence_pattern, :map

change relate_actor(:user)
change manage_relationship(:recurrence_pattern, type: :direct_control)
end
I have tried types create and direct_control
ZachDaniel
ZachDanielβ€’3y ago
So on your recurrence pattern, you probably have a primary create action with a required argument of transaction_id. If that is the case, what you need to do is 1. make that argument optional, 2. use a different create action in your manage relationship
miguels
miguelsOPβ€’3y ago
In recurrence pattern I'm using the default create but based on what you say I suspect the issue could be in the relationship + primary key.
belongs_to :transaction, Transaction do
primary_key? true
attribute_type Ash.Type.Integer
allow_nil? false
attribute_writable? true
end
belongs_to :transaction, Transaction do
primary_key? true
attribute_type Ash.Type.Integer
allow_nil? false
attribute_writable? true
end
I did try setting allow_nil? to true, which Ash obviously didn't like πŸ˜†
ZachDaniel
ZachDanielβ€’3y ago
πŸ€”
miguels
miguelsOPβ€’3y ago
I guess the missing transaction_id is just failing the form validation but the action can do it's job fine. When I add a random transaction_id to the recurrence pattern form it actually works and even sets the correct id
recurrence_pattern: %{
transaction_id: 10000, # this id doesn't actually exist; also tried with nil but that raises Ash.Error.Unknown
recurrence_type_id: context.recurrence_type.id,
count: 5
}
recurrence_pattern: %{
transaction_id: 10000, # this id doesn't actually exist; also tried with nil but that raises Ash.Error.Unknown
recurrence_type_id: context.recurrence_type.id,
count: 5
}
ZachDaniel
ZachDanielβ€’3y ago
πŸ€”πŸ€”πŸ€”πŸ€”πŸ€” This is a pretty standard pattern so im not sure why its not working for you
miguels
miguelsOPβ€’3y ago
It did work last week and stopped working. I changed a lot of stuff so not sure where I broke it. I believe maybe after upgrading Phoenix, LV/Surface and Ash to the latest version
ZachDaniel
ZachDanielβ€’3y ago
Interesting… could you try the version of ash that you were on before? Would be great to confirm that it broke somewhere down the line.
miguels
miguelsOPβ€’3y ago
Tried downgrading to these versions and getting the same error there
{:ash, "~> 2.6.15"},
{:ash_postgres, "~> 1.3.6"},
{:ash_phoenix, "~> 1.2.7"}
{:ash, "~> 2.6.15"},
{:ash_postgres, "~> 1.3.6"},
{:ash_phoenix, "~> 1.2.7"}
It looks like the issue really is setting the transaction_id as the primary key (and therefore also allow_nil? false) If I add integer_primary_key :id to the attributes and remove primary_key? true and allow_nil? false from the transaction relationship the form passes validation and correctly saves all resources. I'd like to keep the table structure as it is if possible, is there a way to tell the form/changeset to not validate the transaction_id? I could also use the :create action directly, but that doesn't sound right.
ZachDaniel
ZachDanielβ€’3y ago
I think this is something we need to fix on our end Basically that required attribute would naturally not be set when creating because the value doesn’t exist yet. So ash forms need to ignore that error. @miguels can you try main for me?
miguels
miguelsOPβ€’3y ago
Hi @Zach Daniel thanks for the update Tried with {:ash, github: "ash-project/ash", branch: "main", override: true} but still getting the same error. Oh but if I remove attribute_writable? true from the relationship it works! Tried it because I spotted private_and_belongs_to? in the commit. I might have been wasting your time πŸ˜… Downgraded back to {:ash, "~> 2.7"} and also tried removing attribute_writable? true from the relationship and it works.
ZachDaniel
ZachDanielβ€’3y ago
Well, it should work with attribute_writable? true Oh, yeah I see why my fix didn’t work okay can you try main again?
miguels
miguelsOPβ€’3y ago
Still seeing the error on the main branch with attribute_writable? true
ZachDaniel
ZachDanielβ€’3y ago
πŸ₯²πŸ₯²πŸ₯² Ah, actually I know why okay, ash main and ash_phoenix main πŸ™‚ LMK if that works
miguels
miguelsOPβ€’3y ago
Sorry still seeing the same error
ZachDaniel
ZachDanielβ€’3y ago
okay, okay, okay. Going to try one more thing. I'm actually technically on vacation which is why I've been ducking in and out trying to fix this instead of taking my time πŸ˜† sorry about that lemme runs ome checks and then I'll push it up okay update ash_phoenix main again
miguels
miguelsOPβ€’3y ago
Now I feel bad for making you work during your vacation πŸ˜…
ZachDaniel
ZachDanielβ€’3y ago
no worries, you didn't make me πŸ˜„ I just know this issue will affect other users as well so I want to get it handled πŸ™‚
miguels
miguelsOPβ€’3y ago
Tests are passing πŸŽ‰
ZachDaniel
ZachDanielβ€’3y ago
:Fire: excellent I'll release new versions of those packages soon
miguels
miguelsOPβ€’3y ago
Thanks a lot for taking the time This also taught me things I didn't know about Ash
ZachDaniel
ZachDanielβ€’3y ago
πŸ₯³ glad to be of help.

Did you find this page helpful?