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?