mathieuprog
Changeset Helpers – Ease working with nested Changesets and associations
Anyone who had to deal with change-ing nested associations knows how strugglesome and tedious it can get.
This library provides for now three convenient functions.
- Nest a change into a changeset:
ChangesetHelpers.put_assoc(account_changeset, [:user, :config, :address], address_changeset)
A function may also be provided as third argument, which receives the nested changeset (or data wrapped by a new changeset) as argument and returns the modified changeset.
For example in the code below I change an empty entity and add it into the association (typically when you want to insert a new row of inputs in a form to add an entity into a collection of persisted entities):
ChangesetHelpers.put_assoc(account_changeset, [:user, :articles],
&(Enum.concat(&1, [%Article{} |> Ecto.Changeset.change()])))
- Change a nested association:
{account_changeset, address_changeset} =
ChangesetHelpers.change_assoc(account_changeset, [:user, :user_config, :address])
{account_changeset, address_changeset} =
ChangesetHelpers.change_assoc(account_changeset, [:user, :user_config, :address], %{street: "Foo street"})
A tuple is returned containing the modified root changeset and the association changeset.
- Check whether a given field is different between two changesets:
{street_changed, street1, street2} =
ChangesetHelpers.diff_field(account_changeset, new_account_changeset, [:user, :user_config, :address, :street])
It’s quite niche but I still wanted to share the work with you ![]()
Most Liked
mathieuprog
I added a changeset utility function raise_if_invalid_fields(changeset, fields).
For example:
changeset
|> validate_inclusion(:cardinal_direction, ["north", "east", "south", "west"])
|> raise_if_invalid_fields([:cardinal_direction])
As its name suggests, the function raises if one of the given fields was provided with an invalid value.
Motivation/use case:
Say you have a dropdown list from which a user may select a value (like a <select> element). If the change is not included in the list, what happened?
It’s either a bug in the application, or a script, a malicious user or bot that bypassed the form control inputs.
As the very first version of my application will definitely have bugs, I want to raise to make sure that I don’t miss such a bug (if it were a bug); because if I just add a changeset error, I will not be automatically alerted of the bug, and have to rely on the user to report such cases.
If scripts or bots are abusing my forms, I can just remove the function call and let the error in the changeset (you don’t want to let your application because of a user submit).
Just an idea. Any thoughts welcome:)
mathieuprog
Added fetch_field(changeset, keys) and fetch_change(changeset, keys) (and their bang ! versions).
street = ChangesetHelpers.fetch_field!(account_changeset, [:user, :config, :address, :street])
mathieuprog
Version 0.17 is released:
-
dropped
raise_if_invalid_fields/2 -
added
field_fails_validation?/3andfield_violates_constraint?/3
Use case: sometimes instead of returning changeset errors to the client, you want to return a specialized response for some of the errors that happened. This is especially useful for returning specialized GraphQL objects representing errors in some cases, instead of a set of generic changeset errors.
Example:
case Accounts.register_user(input) do
# ...
{:error, changeset} ->
email_already_used? =
ChangesetHelpers.field_fails_validation?(changeset, :email, :unsafe_unique)
|| ChangesetHelpers.field_violates_constraint?(changeset, :email, :unique)
if email_already_used? do
# ...
else
{:error, changeset}
end
end
Popular in Announcing
Other popular 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
- #phoenix_html
- #iex
- #blog-post
- #graphql
- #genstage
- #ai
- #websockets
- #supervisor
- #advent-of-code
- #elixirconf-us
- #distillery
- #processes
- #forms
- #api
- #metaprogramming
- #security
- #performance








