mathieuprog

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.

  1. 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()])))
  1. 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.

  1. 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 :grin:

Most Liked

mathieuprog

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

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

mathieuprog

Version 0.17 is released:

  • dropped raise_if_invalid_fields/2

  • added field_fails_validation?/3 and field_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

Where Next?

Popular in Libraries Top

mhanberg
I just released the first version of Temple: an HTML DSL for Elixir and Phoenix! You can read this blog post or the docs for more info...
New
hpopp
After just over two years in development, this latest version of Pigeon is what I finally consider done in regards to my original vision ...
New
devonestes
Introducing assertions, the library that helps you write really great test assertions! GitHub: https://github.com/devonestes/assertions ...
New
OvermindDL1
Been making an MLElixir thing (not released yet…) for fun in spare time in the past day. I’m just trying to see how much I can get an ML...
132 13487 106
New
pkrawat1
Presenting Aviacommerce, open source e-commerce platform in Elixir Aviacommerce is an open source e-commerce platform in Elixir. We at...
New
Qqwy
While not as prevalent as in imperative languages, arrays (collections with efficient random element access) are still very useful in Eli...
New
mischov
import Meeseeks.CSS html = HTTPoison.get!("https://news.ycombinator.com/").body for story &lt;- Meeseeks.all(html, css("tr.athing")) do...
New
wojtekmach
Hey everyone! Req is an HTTP client for Elixir that I’ve been working on for quite some time. There is already a lot of HTTP clients out...
New
versilov
Could not wait for the missing Elixir ML libraries to appear, so, I wrote one myself, taking https://github.com/sdwolfz/exlearn as a foun...
New
bluzky
You may know https://ui.shadcn.com/, a UI component library for React. I really love it’s design style and components. I’ve built some co...
381 12391 119
New

Other popular topics Top

dotdotdotPaul
Okay, I'm having a heck of a time trying to figure out how to best handle the validation of belongs_to associations in Ecto. I'm sure I'...
New
pmjoe
I have a relationship of love and hate with Elixir. Lots of things are just absolutely right, but there are some things that are kind of ...
New
aalberti333
As the title describes, I’m trying to run Enum.map() over a list of key/value pairs, where the value is a map. My data looks like this: ...
New
malloryerik
Hi, this is for people who, like me, have had some friction using .html.heex templates in VSCode. The solution seems to be, in a hyphena...
New
myronmarston
The Elixir Typespec docs show the following syntax for keyword lists in typespecs: # ... | [key: type] # keyword lis...
New
ashish173
I am using Ecto timestamps with postgres, I can see the timestamps() use the :naive_dateime but for my use case I wanted to store the ti...
New
fayddelight
I tried installing elixir 1.11.2 erlang 23.3.4 via asdf in my zsh shell. Enabled the versions locally and globally. When I list them ...
New
fireproofsocks
Forgive me if this is obvious, but how does one delete a database record WITHOUT selecting it first? https://hexdocs.pm/ecto/Ecto.Repo.h...
New
johnnyicon
Hi all, I've just started learning Elixir and Phoenix Framework, so please pardon my n00bness at this stage. I'm trying to use Postg...
New
baxterw3b
Hi guys, i’m new in the Elixir world, and i have to say, that i love it! i’m having some problem to understand anonymous functions with ...
New

Sub Categories:

We're in Beta

About us Mission Statement