Ecto.Multi - 插入两条相关记录
Ecto.Multi - insert two related records
我正在尝试插入两条相关记录。当我尝试创建一个新用户时。我想先在 pools
table 中创建一条记录,然后将它的 ID 传递给 users' table
pool_id`。我正在尝试使用 Ecto.Multi,但没有成功。我将不胜感激指导或任何帮助!提前致谢!我从未使用过 Ecto.Multi,因此,我将不胜感激您的解释。最后有错误。
这是我的用户模式:
schema "users" do
field :email, :string
field :full_name, :string
field :password_digest, :string
belongs_to :role, EmployeeRewardApp.Role
has_one :pool, EmployeeRewardApp.Points.Pool
timestamps()
# Virtual Fields
field :password, :string, virtual: true
field :password_confirmation, :string, virtual: true
end
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:full_name, :email, :password, :password_confirmation, :role_id])
|> validate_required([:full_name, :email, :password, :password_confirmation, :role_id])
|> cast_assoc(:pool)
end
池架构:
schema "pools" do
field :starting_points, :integer
field :used_points, :integer
belongs_to :user, EmployeeRewardApp.Accounts.User
timestamps()
end
@doc false
def changeset(pool, attrs) do
pool
|> cast(attrs, [:starting_points, :used_points])
|> validate_required([:starting_points])
end
并创建函数:
def create(conn, %{"user" => user_params}) do
alias Ecto.Multi
Multi.new
|> Multi.insert(:pool, %Pool{starting_points: 50, used_points: 0})
|> Multi.merge(fn %{pool: pool} ->
user_pool_relation_multi(pool.id, conn, %{"user" => user_params})
end)
|> Repo.transaction()
end
def user_pool_relation_multi(pool_id, conn, %{"user" => user_params}) do
alias Ecto.Multi
Multi.new
|> Multi.insert(:user, User.changeset(%User{}, user_params))
|> Repo.transaction()
end
现在的错误是:
[error] #PID<0.1477.0> running EmployeeRewardAppWeb.Endpoint (connection #PID<0.1476.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /users
** (exit) an exception was raised:
** (Postgrex.Error) ERROR 42703 (undefined_column) column "user_id" of relation "pools" does not exist
query: INSERT INTO "pools" ("starting_points","user_id","inserted_at","updated_at") VALUES (,,,) RETURNING "id"
(ecto_sql 3.6.2) lib/ecto/adapters/sql.ex:760: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto 3.6.2) lib/ecto/repo/schema.ex:725: Ecto.Repo.Schema.apply/4
(ecto 3.6.2) lib/ecto/repo/schema.ex:350: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(ecto 3.6.2) lib/ecto/association.ex:814: Ecto.Association.Has.on_repo_change/5
(ecto 3.6.2) lib/ecto/association.ex:554: Ecto.Association.on_repo_change/7
(elixir 1.12.2) lib/enum.ex:2385: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto 3.6.2) lib/ecto/association.ex:532: Ecto.Association.on_repo_change/4
(ecto 3.6.2) lib/ecto/repo/schema.ex:873: Ecto.Repo.Schema.process_children/5
(ecto 3.6.2) lib/ecto/multi.ex:716: Ecto.Multi.apply_operation/5
(elixir 1.12.2) lib/enum.ex:2385: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto 3.6.2) lib/ecto/multi.ex:690: anonymous fn/5 in Ecto.Multi.apply_operations/5
(ecto_sql 3.6.2) lib/ecto/adapters/sql.ex:1017: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.4.0) lib/db_connection.ex:844: DBConnection.transaction/3
(ecto 3.6.2) lib/ecto/repo/transaction.ex:20: Ecto.Repo.Transaction.transaction/4
(ecto 3.6.2) lib/ecto/multi.ex:696: Ecto.Multi.apply_operation/5
(elixir 1.12.2) lib/enum.ex:2385: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto 3.6.2) lib/ecto/multi.ex:690: anonymous fn/5 in Ecto.Multi.apply_operations/5
(ecto_sql 3.6.2) lib/ecto/adapters/sql.ex:1017: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.4.0) lib/db_connection.ex:1512: DBConnection.run_transaction/4
(ecto 3.6.2) lib/ecto/repo/transaction.ex:20: Ecto.Repo.Transaction.transaction/4
此外,当我将 starting_points: 50
更改为 "starting_points" => pool.starting_points
(因此它是从表单设置的)时,它说:
key "starting_points" not found
参数:
user %{"email" => "test.999@test.com", "full_name" => "Test Test", "password" => "Test", "password_confirmation" => "Test", "pool" => %{"starting_points" => "80"}, "role_id" => "1"}
如您在架构中所见。我有 table 池 stating_points 和 used_points,用户 table 有用户信息和 pool_id 字段。我想要做的是:创建新用户时,将新记录插入到池 table 中,表单中给出的值为 starting_points,used_points 设置为 0。然后将其他参数插入用户table 在池 table 中新创建的 ID。该错误表明 pool_id 不在池 table 中。这个错误和 Ecto.Multi 让我对如何创建新用户同时在池 table.
中设置起点感到困惑
CreatePoolsMigration
def change do
create table(:pools) do
add :starting_points, :integer
add :used_points, :integer
timestamps()
end
end
创建用户
def change do
create table(:users) do
add :full_name, :string
add :email, :string
add :password_digest, :string
timestamps()
end
end
AddPoolIDToUsers
def change do
alter table(:users) do
add :pool_id, references(:pools)
end
create index(:users, [:pool_id])
end
我看到您已将 pool_id
列添加到用户 table。现在您遇到的错误消息是 user_id
列在池 table.
中不存在
Ecto 认为您在池 table 中有一个 user_id
列,因为您的架构中有这一行 belongs_to :user, EmployeeRewardApp.Accounts.User
.
belongs_to
和 has_one
之间的区别在于 belongs_to
关系进入保存外键的模式(在本例中又称为 user_id)。检查此部分的Ecto docs on Hexdocs.pm。这似乎是这种关系的正确方向,因此使用迁移从用户 table 中删除 pool_id 并将 user_id 添加到池 table.
或者,您可以在模式中翻转它,将一个用户 belongs_to 放在一个池中,一个池有一个用户,但我认为您会发现这是一种非常规的关联方式table秒。最好的建议是尽可能遵守惯例,尤其是在学习新技术时。
使用这个策略,你甚至不需要使用Multi来插入池记录,因为你已经有了用户的ID号,你可以只插入一行到池中table ,而不是插入池行然后获取该行的 ID 并更新用户 table.
我正在尝试插入两条相关记录。当我尝试创建一个新用户时。我想先在 pools
table 中创建一条记录,然后将它的 ID 传递给 users' table
pool_id`。我正在尝试使用 Ecto.Multi,但没有成功。我将不胜感激指导或任何帮助!提前致谢!我从未使用过 Ecto.Multi,因此,我将不胜感激您的解释。最后有错误。
这是我的用户模式:
schema "users" do
field :email, :string
field :full_name, :string
field :password_digest, :string
belongs_to :role, EmployeeRewardApp.Role
has_one :pool, EmployeeRewardApp.Points.Pool
timestamps()
# Virtual Fields
field :password, :string, virtual: true
field :password_confirmation, :string, virtual: true
end
@doc false
def changeset(user, attrs) do
user
|> cast(attrs, [:full_name, :email, :password, :password_confirmation, :role_id])
|> validate_required([:full_name, :email, :password, :password_confirmation, :role_id])
|> cast_assoc(:pool)
end
池架构:
schema "pools" do
field :starting_points, :integer
field :used_points, :integer
belongs_to :user, EmployeeRewardApp.Accounts.User
timestamps()
end
@doc false
def changeset(pool, attrs) do
pool
|> cast(attrs, [:starting_points, :used_points])
|> validate_required([:starting_points])
end
并创建函数:
def create(conn, %{"user" => user_params}) do
alias Ecto.Multi
Multi.new
|> Multi.insert(:pool, %Pool{starting_points: 50, used_points: 0})
|> Multi.merge(fn %{pool: pool} ->
user_pool_relation_multi(pool.id, conn, %{"user" => user_params})
end)
|> Repo.transaction()
end
def user_pool_relation_multi(pool_id, conn, %{"user" => user_params}) do
alias Ecto.Multi
Multi.new
|> Multi.insert(:user, User.changeset(%User{}, user_params))
|> Repo.transaction()
end
现在的错误是:
[error] #PID<0.1477.0> running EmployeeRewardAppWeb.Endpoint (connection #PID<0.1476.0>, stream id 1) terminated
Server: localhost:4000 (http)
Request: POST /users
** (exit) an exception was raised:
** (Postgrex.Error) ERROR 42703 (undefined_column) column "user_id" of relation "pools" does not exist
query: INSERT INTO "pools" ("starting_points","user_id","inserted_at","updated_at") VALUES (,,,) RETURNING "id"
(ecto_sql 3.6.2) lib/ecto/adapters/sql.ex:760: Ecto.Adapters.SQL.raise_sql_call_error/1
(ecto 3.6.2) lib/ecto/repo/schema.ex:725: Ecto.Repo.Schema.apply/4
(ecto 3.6.2) lib/ecto/repo/schema.ex:350: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
(ecto 3.6.2) lib/ecto/association.ex:814: Ecto.Association.Has.on_repo_change/5
(ecto 3.6.2) lib/ecto/association.ex:554: Ecto.Association.on_repo_change/7
(elixir 1.12.2) lib/enum.ex:2385: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto 3.6.2) lib/ecto/association.ex:532: Ecto.Association.on_repo_change/4
(ecto 3.6.2) lib/ecto/repo/schema.ex:873: Ecto.Repo.Schema.process_children/5
(ecto 3.6.2) lib/ecto/multi.ex:716: Ecto.Multi.apply_operation/5
(elixir 1.12.2) lib/enum.ex:2385: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto 3.6.2) lib/ecto/multi.ex:690: anonymous fn/5 in Ecto.Multi.apply_operations/5
(ecto_sql 3.6.2) lib/ecto/adapters/sql.ex:1017: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.4.0) lib/db_connection.ex:844: DBConnection.transaction/3
(ecto 3.6.2) lib/ecto/repo/transaction.ex:20: Ecto.Repo.Transaction.transaction/4
(ecto 3.6.2) lib/ecto/multi.ex:696: Ecto.Multi.apply_operation/5
(elixir 1.12.2) lib/enum.ex:2385: Enum."-reduce/3-lists^foldl/2-0-"/3
(ecto 3.6.2) lib/ecto/multi.ex:690: anonymous fn/5 in Ecto.Multi.apply_operations/5
(ecto_sql 3.6.2) lib/ecto/adapters/sql.ex:1017: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
(db_connection 2.4.0) lib/db_connection.ex:1512: DBConnection.run_transaction/4
(ecto 3.6.2) lib/ecto/repo/transaction.ex:20: Ecto.Repo.Transaction.transaction/4
此外,当我将 starting_points: 50
更改为 "starting_points" => pool.starting_points
(因此它是从表单设置的)时,它说:
key "starting_points" not found
参数:
user %{"email" => "test.999@test.com", "full_name" => "Test Test", "password" => "Test", "password_confirmation" => "Test", "pool" => %{"starting_points" => "80"}, "role_id" => "1"}
如您在架构中所见。我有 table 池 stating_points 和 used_points,用户 table 有用户信息和 pool_id 字段。我想要做的是:创建新用户时,将新记录插入到池 table 中,表单中给出的值为 starting_points,used_points 设置为 0。然后将其他参数插入用户table 在池 table 中新创建的 ID。该错误表明 pool_id 不在池 table 中。这个错误和 Ecto.Multi 让我对如何创建新用户同时在池 table.
中设置起点感到困惑CreatePoolsMigration
def change do
create table(:pools) do
add :starting_points, :integer
add :used_points, :integer
timestamps()
end
end
创建用户
def change do
create table(:users) do
add :full_name, :string
add :email, :string
add :password_digest, :string
timestamps()
end
end
AddPoolIDToUsers
def change do
alter table(:users) do
add :pool_id, references(:pools)
end
create index(:users, [:pool_id])
end
我看到您已将 pool_id
列添加到用户 table。现在您遇到的错误消息是 user_id
列在池 table.
Ecto 认为您在池 table 中有一个 user_id
列,因为您的架构中有这一行 belongs_to :user, EmployeeRewardApp.Accounts.User
.
belongs_to
和 has_one
之间的区别在于 belongs_to
关系进入保存外键的模式(在本例中又称为 user_id)。检查此部分的Ecto docs on Hexdocs.pm。这似乎是这种关系的正确方向,因此使用迁移从用户 table 中删除 pool_id 并将 user_id 添加到池 table.
或者,您可以在模式中翻转它,将一个用户 belongs_to 放在一个池中,一个池有一个用户,但我认为您会发现这是一种非常规的关联方式table秒。最好的建议是尽可能遵守惯例,尤其是在学习新技术时。
使用这个策略,你甚至不需要使用Multi来插入池记录,因为你已经有了用户的ID号,你可以只插入一行到池中table ,而不是插入池行然后获取该行的 ID 并更新用户 table.