def create(conn, %{"user" => user_params}) do
changeset = User.changeset(%User{}, user_params, :signin)
if changeset.valid? do
redirect conn, to: page_path(conn, :index)
else
render(conn, "new.html", changeset: changeset)
end
You usually don’t just check that the changeset is valid, you do something with it.
The expected usage is something like this:
case Repo.insert(changeset) do
{:ok, _model} ->
conn
|> redirect(to: page_path(conn, :index))
{:error, changeset} ->
# Here the changeset has had an action attribute set
conn
|> render("new.html", changeset: changeset)
end
The form helper checks that an action was attempted on the changeset.
You can force errors to be shown by adding an action before sending it to the page.
Sorry to bump up an old thread but can someone explain why it’s not working though? I need to manually validate and show the errors because my form is a little complex (I use external api to map some fields).
I got it. I couldn’t find it in the documentation so I started debugging it myself using IO.puts and finding the differences. (Let me know if you have better debugging techniques you would like to share)
I’m not really sure why a changeset is being used here at all. It appears that you’re using a changeset to verify whether a user can be logged in? Changesets are for database operations and that’s all.
Forcing on an action key on to the struct is very bad practice. When used properly, changesets will configure action themselves.
If you’re being RESTful, you should define a SessionController and point your login form there.
defmodule YourApp.SessionController do
use YourApp.Web, :controller
# Render the login form in new.html
def new(conn, _params) do
render(conn, "new.html")
end
# Handle a login attempt
# POST /sessions
def create(conn, %{"user" => user_params}) do
case perform_your_password_check_here do
{:ok, user} ->
conn
|> Guardian.Plug.sign_in(user) # Or the equivalent of whatever auth lib you're using
|> put_flash(:info, "Signed in successfully")
|> redirect(to: "/")
:error ->
conn
|> put_flash(:info, "Email or password incorrect")
|> redirect(to: session_path(conn, :new))
end
end
# Log the user out
# DELETE /session
def delete(conn, _params) do
conn
|> Guardian.Plug.sign_out
|> put_flash(:info, "Signed out successfully")
|> redirect(to: session_path(conn, :new))
end
end
Changesets are not only for database operations. You can use Ecto schemas to map data coming from any source, not only the database, and consequently use changesets to cast and validate changes you want to apply to any of those data sources. Ecto 2.0 even generalized things a bit to allow us to map and cast data even without having a schema. There is a bit of info here: Ecto’s insert_all and schemaless queries « Plataformatec Blog
We probably can improve the docs surrounding this area quite a lot.
Hah, I’d actually played with putting my permission system in a Ecto Changeset compatible thing but decided against it as it seemed I was putting different ideas together that should not be. Glad to see it is actually designed that way. ^.^
I tried to follow this (and the docs) and I get response tellimg me that: non-struct data in changeset requires the :as option given. I have params like: Parameters: %{"_csrf_token" => "[FILTERED]", "session" => %{"email" => "", "password" => "[FILTERED]"}} actually both fields are empty there.
I create the changeset:
data = %{}
types = %{email: :string, password: :string}
changeset =
{data, types} # The data+types tuple is equivalent to [...]
|> Ecto.Changeset.cast(params["session"], Map.keys(types))
|> Ecto.Changeset.validate_required([:email, :password])