AE
Ash Elixir•3y ago
Blibs

Ash.Changeset.change_attribute before or after action

I have this in my seeds.exs file:
Accounts.User
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, args)
|> Ash.Changeset.change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.change_attribute(:roles, [:super_admin])
|> Accounts.create!()
Accounts.User
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, args)
|> Ash.Changeset.change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.change_attribute(:roles, [:super_admin])
|> Accounts.create!()
What I want to do with this is register a new super user, but I don't have and don't want a specific action to create one since I see it as something that only should be done once in the seeds step. So what I did was create the changeset that will run my action register_with_password, and then I just manually fill the fields that are missing (confirmed_at and roles in this case). This works fine, but I get warnings saying that this will stop working in the future:
warning: Changeset has already been validated for action :register_with_password.

In the future, this will become an error.

For safety, we prevent any changes after that point because they will bypass validations or other action logic.. To proceed anyway,
you can use `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(...)
warning: Changeset has already been validated for action :register_with_password.

In the future, this will become an error.

For safety, we prevent any changes after that point because they will bypass validations or other action logic.. To proceed anyway,
you can use `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(...)
At the same time, if I change the order and move the change_attribute calls up, then my seeds stop working with the following error:
* Invalid value provided for roles: cannot be changed.

[:super_admin]

(ash 2.6.3) lib/ash/changeset/changeset.ex:995: anonymous fn/2 in Ash.Changeset.validate_attributes_accepted/2
* Invalid value provided for roles: cannot be changed.

[:super_admin]

(ash 2.6.3) lib/ash/changeset/changeset.ex:995: anonymous fn/2 in Ash.Changeset.validate_attributes_accepted/2
and
* Invalid value provided for confirmed_at: cannot be changed.

~U[2023-02-15 14:01:03.132248Z]

(ash 2.6.3) lib/ash/changeset/changeset.ex:995: anonymous fn/2 in Ash.Changeset.validate_attributes_accepted/2
* Invalid value provided for confirmed_at: cannot be changed.

~U[2023-02-15 14:01:03.132248Z]

(ash 2.6.3) lib/ash/changeset/changeset.ex:995: anonymous fn/2 in Ash.Changeset.validate_attributes_accepted/2
What's the correct way to do this?
3 Replies
ZachDaniel
ZachDaniel•3y ago
Use force_change_attribute, and do it after Ash.Changeset.for_create
Blibs
BlibsOP•3y ago
Like this right?
Accounts.User
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, super_user_args)
|> Ash.Changeset.force_change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.force_change_attribute(:roles, [:super_admin])
|> Accounts.create!()
Accounts.User
|> Ash.Changeset.new()
|> Ash.Changeset.for_create(:register_with_password, super_user_args)
|> Ash.Changeset.force_change_attribute(:confirmed_at, DateTime.utc_now())
|> Ash.Changeset.force_change_attribute(:roles, [:super_admin])
|> Accounts.create!()
Worked like a charm, thanks!
ZachDaniel
ZachDaniel•3y ago
yep! 🥳

Did you find this page helpful?