Run an create/update action without persisting the data
Eduardo B. Alexandre:
Is there any way to run a create/update action without persisting it to the db (I’m using postgres)?
frankdugan3:
Are you looking to just get a changeset to inspect, or do you want to persist the data in some custom way? A manual action might be what you’re looking for, as it will give you the changeset and leave what to do with it up to you: https://hexdocs.pm/ash/manual-actions.html
_ahey:
There are also generic actions available that don’t persist. https://www.ash-hq.org/docs/guides/ash/latest/topics/actions#generic-actions
Eduardo B. Alexandre:
Sorry for taking too much time to reply.
So, basically I have the following create action:
create :create do
alias Actions.Create.Changes
primary? true
change Changes.CalculateRentFields
change Changes.CalculateFlipFields
end
The idea is that I will pass some inputs to it and the action will calculate a bunch of other fields (via the changes) before persisting that to the DB.
The thing is that I want to show the result of these calculations before I actually persist it into the DB. I tried to just call
AshPhoenix.Form.validate
but that will just say that the changeset is valid, it will not actually call my changes, so I don’t get the calculations back.
In other words, I want to call that
create
action so I can get back the calculated fields, but I don’t want to actually persist into the DB yet since I want the user to check if the calculated fields are what they expect before actually persisting it (they do that via a LiveView page with a AshPhoenix.Form btw).
Eduardo B. Alexandre:
About using a generic or manual action, I believe that would be possible, but at the same time AFAIK I would not be able to actually get a changeset that I can send to my
change
modules, meaning that I would need to do some “workaround” to actualy use the same calculations in both places. I would prefer to keep using the
change
API if possible.
Eduardo B. Alexandre:
So, I tried to go with the manual route, this is what I have so far:
create :create do
manual MyManualAction
end
defmodule MyManualAction do
@moduledoc false
alias Marketplace.Invoices.ProForma.Actions.Create.Changes
use Ash.Resource.ManualCreate
def create(changeset, opts, context) do
changeset
|> Changes.CalculateRentFields.change(opts, context)
|> Changes.CalculateFlipFields.change(opts, context)
end
end
The thing is that I’m not sure exactly how to implement that create function, I need a function that will actually apply the changeset attributes plus run the two changes I added to it (both add
before_action
hooks). I tried running
Ash.Changeset.apply_attributes()
but that one will not run my change hooks at all.
Eduardo B. Alexandre:
So, I think I figured out a workaround for it. I’m gonna be honest, I’m not very happy with it, but at least it allows me to do what I want until I can figure out a better way.
First, in my changes, I splitted my
change
function into 2 functions, one
change/1
and another one
change/3
:
defmodule Marketplace.Invoices.ProForma.Actions.Create.Changes.CalculateRentFields do
@moduledoc """
Calculate all fields needed for rent information
"""
alias Ash.Changeset
use Ash.Resource.Change
def change(changeset, _opts, _context),
do: Changeset.before_transaction(changeset, &change/1, append?: true)
def change(changeset) do
changeset
|> calculate_rent_cost_basis()
|> calculate_vacancy()
...
end
...
end
With that, I can keep my create action the same way as before:
create :create do
alias Actions.Create.Changes
primary? true
change Changes.CalculateRentFields
change Changes.CalculateFlipFields
end
And I can create a new manual action:
create :create_without_persisting do
transaction? false
manual MyManualAction
end
defmodule MyManualAction do
@moduledoc false
alias Marketplace.Invoices.ProForma.Actions.Create.Changes
use Ash.Resource.ManualCreate
def create(changeset, _opts, _context) do
changeset
|> Changes.CalculateRentFields.change()
|> Changes.CalculateFlipFields.change()
|> Ash.Changeset.apply_attributes()
end
end
Now I can just run the
create_without_persisting
action directly to get all the computed fields and show it in my liveview page in realtime everytime the form validation is called and the form is valid.
\ ឵឵឵:
You can make this generic with an
ApplyAtrributesForAction
manual action, which accepts the action to be simulated as an option.