spacebat
I recently updated from an early Ash 3.x to the most recent, and find at runtime a lot of warnings like:
Changeset has already been validated for action :create.
For safety, we prevent any changes after that point because they will bypass validations or other action logic.. To proceed anyway, you can use `force_change_attribute/3`. However, you should prefer a pattern like the below, which makes any custom changes *before* calling the action.
Resource
|> Ash.Changeset.new()
|> Ash.Changeset.change_attribute(...)
|> Ash.Changeset.for_create(...)
So far in my application I have a lot of actions that use Ash.Changeset.change_attribute/3 within change before_action(fn ....) blocks. This is usually to support computed defaults that depend on other attributes/arguments so that the call sites for the actions are nice and clean.
The simplest thing is to change all these occurrences to Ash.Changeset.force_change_attribute/3 but the warning advises against it.
I could write wrapper functions for all these actions that compute the defaults before calling the action, as advised by the warning, but then the actions themselves become far less convenient to call directly, and the functions will be located elsewhere in the module, not right next to the action they are related to.
Maybe wrapper actions would be better, so have a create :raw_create that defaults nothing, and create :create would accept no attributes, only arguments, compute the defaults and call the raw_create action, but that feels verbose and undesirable.
Validations could be removed from the attributes and applied explicitly within each action, but that would lose a lot of the attribute metadata and be more repetitive and error prone.
I don’t see a change before_validation hook but if it existed that would be a simple fix.
It feels like I’ve been using Ash wrong but it’s unclear what the best approach is, perhaps I’m missing something obvious that I didn’t notice in the docs.
Trending in Questions
Other Trending 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
- #blog-post
- #phoenix_html
- #iex
- #graphql
- #ai
- #genstage
- #elixirconf-us
- #websockets
- #supervisor
- #advent-of-code
- #distillery
- #processes
- #api
- #forms
- #metaprogramming
- #security
- #hex










Showing Posts 1 to 10- Show Best Posts
- Show All Posts (oldest first)
- Show All Posts (newest first)
zachdaniel
Do the before action hooks you’re using have side effects/do anything expensive? If not your best bet is to to just move the code into the body of the cnange.
zachdaniel
Alternatively for actions that have to set values in before_action hooks you can set the validations to be done themselves in before action hooks with
before_action?: trueThen they will happen after your logic to change attributes despite that logic being in a
before_actionhookspacebat
No, at worst they might lazy load something, for the most part it’s things like the run_mode attribute depends on the type of another argument.
zachdaniel
Right, so in that case there is likely no need for a
before_actionhook at all.i.e
can be
The major difference you may notice is that the body of changes runs on both valid and invalid change sets, so you may want to add
change YourChange, only_when_valid?: true, or anif changeset.valid?in your change implementation, or simply handle the fact that the changes may be invalid (so don’t rely on something likechangeset.arguments.whatever, instead useAsh.Changeset.get_argument(changeset, :whatever), and be aware that it can be invalid.This is good though as it allows knowing up front an action will fail and producing multiple errors at a time.
spacebat
I guess that only works with custom actions each in their own module? At the moment they are all just create and update actions within the resource modules.
I noticed that the example for a custom action uses
force_change_attributeanyway: Changes — ash v3.29.3zachdaniel
I’m not sure what you mean. Do you mean that the changes are all in anonymous functions? You can put them in change modules that
use Ash.Resource.Changespacebat
All my actions are of the form:
zachdaniel
To keep that form
spacebat
That’s a bit confusing - the warning is about calling
change_attributein achange before_action, which is post validation. Isn’tchangeeven more post validation?zachdaniel
Nope. Change happens first. It’s the very first thing that happens. It happens on every valid
The general formula looks like this:
Anything that you do in the
changefunction happens at the very beginning after basic input validation, when you run functions likeAsh.Changeset.for_create(…).change before_action(function)is just a built in shorthand that looks like this