thojanssens1
Why do we call the Schema's changeset/2 function with empty attributes to render a new form?
From Programming Ecto’s book:
def new(conn, _params) do
changeset = User.changeset(%User{}, %{})
render(conn, changeset: changeset)
end
Why is the changeset/2 function of schema User called here to render a form the first time?
If I inspect the changeset, it will print something like:
#Ecto.Changeset<
action: nil,
changes: %{},
errors: [
name: {"can't be blank", [validation: :required]},
age: {"can't be blank", [validation: :required]},
# etcetera
],
data: #MyApp.User<>,
valid?: false
>
It looks like we tried to validate a changeset, but seems it doesn’t make sense as we didn’t submit anything yet, it was just the first render of the form.
=> These errors have then been computed (and maybe even translated) for nothing.
I thought then there should be a better way: maybe make a function in the context that just does Ecto.Changeset.change(%User{}) rather than calling the shema’s changeset/2 functions with all the rules and validations.
Most Liked
LostKobrakai
The point here is mostly that it’s easier to build the form integration based on just a single type of data - the changeset - instead of needing to differentiate in every second line between an empty form and one where data was submitted, but there were errors or a form, which already has values, which are meant to be edited. The performance aspect of checking validations for most changeset is probably neglectable, but you could also just do Ecto.Changeset.change(%User{}) if you want to skip validation.
peerreynders
Why is the changeset/2 function of schema User called here to render a form the first time?
Ecto.Changesetimplements thePhoenix.HTML.FormDataprotocol (This is called attention to on p.68 of the beta 9 release of Programming Phoenix ≥ 1.4 - and also at the bottom of the page you reference (p.132) in Programming Ecto).- To be able to “serve” that protocol the changeset has to be tied to the schema structure (in your case
#MyApp.User<>).
So I prefer to have a more clear API
https://media.pragprog.com/titles/wmecto/code/priv/examples/phoenix_forms_01.exs
refers to the code from that section (p.132) of Programming Ecto
# Accounts
def create_user(attrs \\ %{}) do
%User{}
|> User.changeset(attrs)
|> Repo.insert()
end
is supposed to be that clear API.
Also have a look at what Repo.insert does:
defp do_insert(repo, name, %Changeset{valid?: true} = changeset, opts) do
# ...
end
defp do_insert(repo, _name, %Changeset{valid?: false} = changeset, opts) do
{:error, put_repo_and_action(changeset, :insert, repo, opts)}
end
By extension functions that run after validation like your fun_add_some_computed_value_in_the_changeset and fun_add_some_other_computed_value_in_the_changeset should have two clauses: one for %Changeset{valid?: true} to do the actual processing and another for %Changeset{valid?: false} which simply returns the original (invalid) changeset.
That is Railway Oriented Programming (ROP) as practiced with the Elixir pipe operator.







