--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/96d7016a-65f2-476d-a0ba-5581033c47ac%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Thank you for clarifying that. I can send the structure you gave, but it's not secure. When an account and a user are created, I want to ensure the joiner record has a specific role. Obviously, this is not safe to send as a parameter.
--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/455729a4-b756-4388-87eb-83ad1d6ac2b5%40googlegroups.com.
if changeset.valid? do
registration = Ecto.Changeset.apply_changes(changeset)
MyApp.Repo.transaction fn ->
MyApp.Repo.insert_all "accounts", Registration.to_account(registration)
MyApp.Repo.insert_all "profiles", Registration.to_profile(registration)
end
{:ok, registration}
else
changeset = %{changeset | action: :registration}
{:error, changeset}
end
--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/35b3e7bf-8c2a-45a9-a1dd-ed728ca05798%40googlegroups.com.
defmodule App.Registration do use App.Web, :model embedded_schema do field :name field :email field :password field :organisation_name end @required_fields ~w(name email password organisation_name)a
def changeset(struct, params \\ %{}) do
struct |> cast(params, @required_fields) |> validate_required(@required_fields) |> validate_length(:name, max: 30) |> validate_length(:password, min: 8) |> validate_length(:organisation_name, max: 35) |> validate_format(:email, ~r/@/) end end
defmodule App.User do use App.Web, :model schema "users" do field :name, :string field :email, :string field :password_hash, :string field :password, :string, virtual: true end @required_fields ~w(name email password)a
def changeset(struct, params \\ %{}) do
struct |> cast(params, @required_fields) |> validate_required(@required_fields) |> unique_constraint(:email) end end
defmodule App.RegistrationController do use App.Web, :controller alias App.{Account,Registration,User,AccountUser,Repo} def new(conn, _params) do changeset = Registration.changeset(%Registration{}) render conn, :new, changeset: changeset end def create(conn, %{"registration" => registration_params}) do changeset = Registration.changeset(%Registration{}, registration_params) if changeset.valid? do Repo.transaction fn -> case account_changeset(registration_params) |> Repo.insert do {:ok, account} -> case user_changeset(registration_params) |> Repo.insert do {:ok, user} -> %AccountUser{account_id: account.id, user_id: user.id, role: :admin} |> Repo.insert redirect conn, to: registration_path(conn, :new) {:error, user_changeset} -> changeset = copy_errors(user_changeset, changeset) render conn, :new, changeset: %{changeset | action: :insert} end {:error, account_changeset} -> changeset = copy_errors(account_changeset, changeset) render conn, :new, changeset: %{changeset | action: :insert} end end else render conn, :new, changeset: %{changeset | action: :insert} end end defp account_changeset(%{"organisation_name" => org_name}) do Account.changeset(%Account{}, %{name: org_name}) end defp user_changeset(params) do user_params = Map.take(params, ["name", "email", "password"]) User.changeset(%User{}, user_params) end defp copy_errors(from, to) do Enum.reduce from.errors, to, fn {field, {msg, _}}, acc -> Ecto.Changeset.add_error(acc, field, msg) end end end
--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/6a565798-a426-4c18-878b-4f98b9a8b465%40googlegroups.com.
def create(conn, %{"registration" => registration_params}) do changeset = Registration.changeset(%Registration{}, registration_params) if changeset.valid? do
{_, conn} = Repo.transaction fn -> with {:ok, account} <- account_changeset(registration_params) |> Repo.insert, {:ok, user} <- user_changeset(registration_params) |> Repo.insert do %AccountUser{account_id: account.id, user_id: user.id, role: :admin} |> Repo.insert! redirect conn, to: registration_path(conn, :new) else {:error, repo_changeset} -> changeset = copy_errors(repo_changeset, changeset) render conn, :new, changeset: %{changeset | action: :insert} end end
else render conn, :new, changeset: %{changeset | action: :insert} end end
Cleaner for sure. But here's the stack trace from the conosle. This error doesn't appear in the browser; things just work.
[error] #PID<0.489.0> running App.Endpoint terminated
Server: localhost:4000 (http)
Request: POST /registrations
** (exit) an exception was raised:
** (RuntimeError) expected action/2 to return a Plug.Conn, all plugs must receive a connection (conn) and return a connection
(app) web/controllers/registration_controller.ex:1: App.RegistrationController.phoenix_controller_pipeline/2
(app) lib/app/endpoint.ex:1: App.Endpoint.instrument/4
(app) lib/phoenix/router.ex:261: App.Router.dispatch/2
(app) web/router.ex:1: App.Router.do_call/2
(app) lib/app/endpoint.ex:1: App.Endpoint.phoenix_pipeline/1
(app) lib/plug/debugger.ex:123: App.Endpoint."call (overridable 3)"/2
(app) lib/app/endpoint.ex:1: App.Endpoint.call/2
(plug) lib/plug/adapters/cowboy/handler.ex:15: Plug.Adapters.Cowboy.Handler.upgrade/4
(cowboy) src/cowboy_protocol.erl:442: :cowboy_protocol.execute/4
I researched the error and found your answer: The error results from a conn struct not being returned from a controller action. But it's hard to see where this is happening in my create action. As mentioned, it only appears if the if changeset.valid? runs, which means the transaction is running.
I will look into Ecto Multi soon to see how the solution feels.
if changeset.valid? do {_, conn} = Repo.transaction fn -> with {:ok, account} <- account_changeset(registration_params) |> Repo.insert, {:ok, user} <- user_changeset(registration_params) |> Repo.insert do %AccountUser{account_id: account.id, user_id: user.id, role: :admin} |> Repo.insert! redirect conn, to: registration_path(conn, :new) else {:error, repo_changeset} -> changeset = copy_errors(repo_changeset, changeset) render conn, :new, changeset: %{changeset | action: :insert} end end
conn
else
render conn, :new, changeset: %{changeset | action: :insert}
Repo.rollback render conn, :new, changeset: %{changeset | action: :insert}
--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/29470df1-31f4-4ecf-88e3-09676aeb78c8%40googlegroups.com.
--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/13bdae9d-00f5-40cf-93f2-d2eb40334510%40googlegroups.com.
defmodule App.Registration do use App.Web, :model
alias App.{Account, User, Membership, Repo} embedded_schema do field :email field :org_name end @required_fields ~w(email org_name)a
def changeset(struct, params \\ %{}) do
struct |> cast(params, @required_fields) |> validate_required(@required_fields)
# ...
end
# ...
end
def changeset(struct, params \\ %{}) do
struct |> cast(params, @required_fields)
end
def create(conn, %{"registration" => registration_params}) do changeset = Registration.changeset(%Registration
{}, registration_params) case Repo.transaction(Registration.to_multi(registration_params)) do {:ok, _} -> redirect conn, to: registration_path(conn, :new) {:error, _operation, repo_changeset, _changes} -> changeset = copy_errors(repo_changeset, changeset) render conn, :new, changeset: %{changeset | action: :insert} end end
# this part stays
if changeset.valid? do
# this part stays
else
render conn, :new, changeset: %{changeset | action: :insert} end
defmodule Class.Registration do embedded_schema do field :user_name field :account_name end def to_multi(params \\ %{}) do Ecto.Multi.new |> Ecto.Multi.insert(:account, account_changeset(params)) |> Ecto.Multi.insert(:user, user_changeset(params)) # ... end defp account_changeset(params) do # map %{"account_name" => "bar"} to %{name: "foo"} end defp user_changeset(params) do # map %{"user_name" => "bar"} to %{name: "bar"} end end
def to_multi(%{valid?: false)) do
Ecto.Multi.new |> Ecto.Multi.run(:registration, fn _ -> {:error, %{}} end)
end
--
You received this message because you are subscribed to the Google Groups "elixir-ecto" group.
To unsubscribe from this group and stop receiving emails from it, send an email to elixir-ecto+unsubscribe@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/elixir-ecto/642bd82d-5b8b-4828-abf6-e25481f0acbf%40googlegroups.com.
def to_multi(%{valid?: false)) do
Ecto.Multi.new |> Ecto.Multi.error(:registration, %{errors: []})end