Thanks for this @zachdaniel, that was hugely helpful in furthering my understanding of atomics.
I did run headlong into:
%Ash.Error.Framework.CanNotBeAtomic{resource: COE.Walk.Activity, change: COE.Validations.ValidateWithinSprint, reason: "Create actions cannot be made atomic", splode: Ash.Error, bread_crumbs: [], vars: [], path: [], stacktrace: #Splode.Stacktrace<>, class: :framework}
Which I did not realize. I need to implement it as a non-atomic validate/3 function. (And presumably keep the atomic version for, e.g., updates).
The parallel writeup here was also a great read: Does validations in update actions always require you to load the fields the validations are working on - #5 by anagrius.
My first attempt was to try and load/2 the sprint inside the validate/3:
sprint = Ash.load!(changeset.data, [:sprint])
That failed. I’m assuming, because the activity is not yet persisted. But I was a bit surprised. The changeset does have the relationship. I would have thought load/2 would be smart enough but, my understanding is not complete.
So… I proceeded (hopefully correctly) with the following validate/3 function.
def validate(changeset, _, _) do
sprint = Sprint |> Ash.Query.filter(id == ^Ash.Changeset.get_attribute(changeset, :sprint_id)) |> Ash.read_one!()
date = Ash.Changeset.get_attribute(changeset, :date)
if date < sprint.start_date or date > sprint.end_date do
{:error, field: :base, message: "activity date must be between the sprint's range of #{sprint.start_date} and #{sprint.end_date}"}
else
:ok
end
end
The only bit I’m a little uncomfortable with is the read_one call. The above works. I’m just wondering if it is less efficient than it could be, or if there’s a more idiomatic approach.






















