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_tohas_one 之间的区别在于 belongs_to 关系进入保存外键的模式(在本例中又称为 user_id)。检查此部分的Ecto docs on Hexdocs.pm。这似乎是这种关系的正确方向,因此使用迁移从用户 table 中删除 pool_id 并将 user_id 添加到池 table.

或者,您可以在模式中翻转它,将一个用户 belongs_to 放在一个池中,一个池有一个用户,但我认为您会发现这是一种非常规的关联方式table秒。最好的建议是尽可能遵守惯例,尤其是在学习新技术时。

使用这个策略,你甚至不需要使用Multi来插入池记录,因为你已经有了用户的ID号,你可以只插入一行到池中table ,而不是插入池行然后获取该行的 ID 并更新用户 table.