Hi all, I’m currently reading a book on web development in Phoenix and I’m having trouble understanding some of the content. I was hoping you’d be able to help.
I have the following module which defines a Schema for Ecto to work with:
defmodule Vocial.Votes.Poll do
use Ecto.Schema
import Ecto.Changeset
alias Vocial.Votes.Poll
alias Vocial.Votes.Option
schema "polls" do
field :title, :string
has_many :options, Option
timestamps()
end
def changeset(%Poll{}=poll, attrs \\ %{}) do
poll
|> cast(attrs, [:title])
|> validate_required([:title])
end
end
I’m struggling to understand what’s going on in the changeset method. Here are my questions:
%Poll{}=poll is this saying that the first argument is the default/empty struct Poll? If so, then is this the same as writing poll=%Poll{}?
As you can see %Poll{} is based on alias Vocial.Votes.Poll which is also the name of the module where it sits, so it’s referencing itself. However, there is no struct definition here, so I’m not sure what Elixir would expect from a %Poll{} object. Is the struct somehow read from the schema?
Lastly, the validate_required(:title) is there to ensure that we pass the title among the attributes, right? If so, then there is another function in the codebase which I would have thought should break it, but it doesn’t – the function is def new_poll, do: Poll.changeset(%Poll{}) – no title is passed, yet the validator doesn’t complain. Why is that?
So it’s just a type check, right? When I suggested initially that it’s the same as poll=%Poll{} thinking that it’s a default parameter, but I forgot that defaults in Elixir are set with \\.
Ah, nice, that a great suggestion, didn’t realise you can do that!
I think so, unless I’m missing something, but here is some additional code on that:
def new_poll do
Poll.changeset(%Poll{}) # no title!
end
...
def new(conn, _params) do
poll = Votes.new_poll()
conn
|> render("new.html", poll: poll)
end
Yes, you should see the errors when inspecting @poll. Just an additional note that Phoenix forms work with the action value in order to display errors in the UI. You will probably see that action is currently nil. However, calling something like Repo.update or Repo.insert on that invalid changeset will return a changeset with action set to :update or :insert. If Phoenix has a changeset with the action set it displays errors on the form for the appropriate inputs.
You can do the same manually by calling Ecto.Changeset.apply_action(changeset, :update) (or whatever action makes sense). If there are errors, a tuple will be returned with {:error, changeset}. This provides for a lot of flexibility and power to validate data outside database calls.
I’m sure that Phoenix book will be great from a high-level perspective, but I would also suggest reading this page about Ecto changesets; it is well-written and helps fill in a lot of the gaps… https://hexdocs.pm/ecto/Ecto.Changeset.html