Ash Relationships with existing Ecto Repos
fekle:
Hi there, I’m currently working on integrating Ash into my existing Phoenix/Ecto/Postgres app. So far Ash looks amazing and seems to be exactly what I’m looking for - the power and balance of ecto but without having to create basic utility functions over and over and over again 😉
Sorry if my question has already been answered, I couldn’t find anything on GH, google, docs or Discord.
However I don’t yet want to migrate all my existing Ecto (Phoenix Context) resources all at once, but go step by step. Now, I added a new Repo, Registry and API with a new resource and that works fine. However now I want to add a :belongs_to in my new resource’s :relationships - and that needs to be a user, so the resource belongs to a user. Thing is, the User resource already exists as a normal Ecto schema that is used in my app, so ash tells me it can’t find the resource. Is there some way to have a “compatibility” layer between an existing ecto schema and an ash resource?
Thanks!
ZachDaniel:
Unfortunately there is nothing that lets you directly relate ecto schemas and ash resources at this time
fekle:
I understand, thanks for your swift reply! So i’ll just use a normal uuid field for now and manage the relationship manually, and when I migrate my user resource i should be able to reuse the existing uuid field.
ZachDaniel:
There also isn’t really a downside to having a duplicate ash resource for your ecto schema
fekle:
so that means i could have a new ash resource for my users which uses the same postgres table as my existing ecto schema, and keep using my ecto schema for existing functions? sounds like a good compromise
ZachDaniel:
Yep! Others have done pretty much exactly that.
fekle:
thanks a lot! I will do that 🙂
fekle:
One more quick question that I couldnt find the answer for:
When using AshPhoenix.Form, is it possible to add values that are always applied when validating or submitting the form? For example a user id? I know that i can do this with a hidden input or by modifying the
params
before validating/submitting, but is there a more idiomatic way? The “params” option gets lost after the first validate.
ZachDaniel:
You can use the
prepare_source
option
ZachDaniel:
Form.for_create(..., prepare_source: fn changeset ->
Ash.Changeset.set_argument(....)
end)
ZachDaniel:
Actually I think in your case you want
before_submit
which is an option you add to the form submission
fekle:
Ah, i saw that before but that didnt work, as i want to set an attribute and not an argument. But using Ash.Changeset.change_attribute/3 in this works.
ZachDaniel:
Form.submit(..., before_submit: fn changeset ->
...
end)
ZachDaniel:
I’d suggest doing it with
before_submit
to avoid malicious things like the user including the field and your value getting overwritten
ZachDaniel:
prepare_source -> put users params into action -> before_submit
fekle:
I understand, yeah thats the reason I dont like using hidden fields etc.
fekle:
before_submit unfortunately only works with submit and not validate, i need the attribute for validation
ZachDaniel:
🤔 interesting…maybe use both?
ZachDaniel:
I think there are definitely some ergonomics to be improved there.
fekle:
yes some kind of “unmodifyable values” that are always overwritten in the params. Maybe I’ll have a look at opening a PR for that 🙂
ZachDaniel:
So you need the attribute for validation because otherwise the form says its invalid, right?
ZachDaniel:
because you can technically submit an invalid form with
force?: true
on the submit
ZachDaniel:
so you could just let the form be invalid until then?
fekle:
yes, its a bit of an edge case. the item in the form belongs to a user, and has a number field - but the max value of the number field depends on a value in the user profile, so i need it for validation
ZachDaniel:
interesting. Then yeah I think you might be right. If the form accepts some values on form creation/validation like
immutable_values: ...
that would solve for it.
fekle:
In the meanwhile i’m thinking that maybe using an argument and handling the value in my actions is the better way do do this. there i could use the argument user_id for my validation and then always setting the attribute based on that argument
fekle:
thank you very much for your help, that cleared up things for me! 🙂