Examples of good style for more complicated changesets and/or changeset-function builders

Newbie here. I’m looking for both advice on idiomatic changeset code and pointers to projects that are good examples of idiomatic code. I worry that my habits don’t match Phoenix+Ecto.


How often do people have multiple changeset-creation functions for a schema? For example, consider an administrator creating a user:

  • The auth_id is required
  • The display_name is required

We want a changeset function with validate_required([:auth_id, :display_name, ...])

But now consider that user changing some of her attributes. She can’t change the auth_id (let’s assume that’s a business requirement, so not up for debate). So I do not want :auth_id in the list given to validate_required. Well, I could alternately put the :auth_id as a hidden field on the “change your settings” form and use the same changeset, but I’m doubtful that’s the right place for the responsibility. It seems to me that the fact “the auth_id cannot change” should be stated in the user.ex file.

My impulse - which may be wrong - is that each field in a schema should have its invariants declared somewhere separate from the other fields (the same way its type and default value are). That could be as a function for each field:

  defp check_attr(:auth_id = field, changeset) do
    changeset
    |> validate_length(field, min: 5, max: 100, count: :codepoints)
    |> unique_constraint(field, name: :unique_auth_id)
  end

… which means that the changeset function could call the appropriate different invariant-checker for each of the required fields.

As a longtime Lisp/Ruby/Clojure weenie, I’m inclined to want some DSL to describe invariants, something like this:

  param_handling(
    auth_id: [
      length: {5, 100},
      unique: :unique_auth_id
    ],
    display_name: [
      length: {2, 100},
    ],
    email: [
      :length: {5, 100},
    ],
...

… and then the creational changeset would be responsible for describing which fields were required or optional, not for what each field should look like.

Should I stop fussing with changeset ideas and just do things the idiomatic way? What is that idiomatic way?

Hey marick,
I think using hidden attribute for auth_id is not a good idea, as you are using the same change set, I can easily edit the hidden attribute and send a different value, if its unique your changeset will allow that to save. if you insist on using the same changeset you can use different cast and validation variables and call accordingly.
@admin_user_fields [:auth_id, :fname]
@user_fields [:fname]

In my current project what i did is, "
there are two modules user and profile.

  • User is created on sign up with ‘pow’ which has access to email, password,
    password_confirmation and related fields. which communicates with user table

  • Profile another module where user can add/update fname, lname, dob, avatar… and this again communicates with users table. profile module doesn’t have access to email & password.

Later i added fname, lname to user schema, such that i can print fullname on top bar.

Now there are three ways,

  1. Use variables (use pattern match)
  2. Use two different changeset
  3. Use two modules admin/users/ and users/

I hope it helps, let me know if you any doubts.

1 Like