【问题标题】:Getting Ecto to pass foreign keys让 Ecto 传递外键
【发布时间】:2019-07-21 07:08:42
【问题描述】:

我是第一次使用 Ecto(使用 Postgres),我有以下两种模式(都有些简化):

defmodule RailroadServer.Database.RailroadSystem do
  @moduledoc """
  Schema for an entire railroad system.
  """
  use Ecto.Schema
  import Ecto.Changeset
  alias RailroadServer.Database

  schema "railroad_systems" do
    field :railroad_system_name, :string
    has_many :depos, Database.Depo
  end

  @fields ~w(railroad_system_name)a

  def changeset(data, params \\ %{}) do
    data
    |> cast(params, @fields)
    |> validate_required([:railroad_system_name])
    |> validate_length(:railroad_system_name, max: 50)
  end
end
defmodule RailroadServer.Database.Depo do
  @moduledoc """
  Schema for a node that stores trains.
  """
  use Ecto.Schema
  import Ecto.Changeset
  alias RailroadServer.Database

  schema "depos" do
    field :capacity, :integer
    field :depo_uuid, :string
    field :depo_name, :string
    belongs_to :railroad_system, Database.RailroadSystem
  end

  @fields ~w(capacity depo_uuid depo_name)a

  def changeset(data, params \\ %{}) do
    data
    |> cast(params, @fields)
    |> validate_required([:capacity, :depo_uuid, :depo_name])
    |> validate_number(:capacity, greater_than: 0)
    |> validate_length(:depo_name, max: 50)
    |> validate_length(:depo_uuid, max: 50)
    |> foreign_key_constraint(:railroad_system_id)
  end
end

基于这些迁移:

defmodule RailroadServer.Database.Repo.Migrations.CreateRailroadSystems do
  use Ecto.Migration

  def change do
    create table(:railroad_systems) do
      add :railroad_system_name, :varchar, null: false, size: 50
    end

    create unique_index("railroad_systems", [:railroad_system_name])
  end
end
defmodule RailroadServer.Database.Repo.Migrations.CreateDepos do
  use Ecto.Migration

  def change do
    create table(:depos) do
      add :railroad_system_id, references("railroad_systems"), null: false
      add :depo_uuid, :varchar, size: 50, null: false
      add :depo_name, :varchar, size: 50, null: false
      add :capacity, :integer, null: false
    end

    create index("depos", [:railroad_system_id])
    create index("depos", [:depo_uuid], unique: true)
    create index("depos", [:depo_name], unique: true)
  end
end

我正在使用以下代码构建:

  def insert_railway_system(system_name, depos) do
    cs = %RailroadSystem{}
    |> RailroadSystem.changeset(%{railroad_system_name: system_name})
    |> put_assoc(:depos, create_depos(depos))

    if cs.valid? do
      Repo.insert(cs)
    else
      {:error, cs}
    end
  end

  _ = """
  Uses a list of depo nodes to construct a list of depo changeset.
  """
  defp create_depos(depos) do
    Enum.map(depos, fn(depo) -> Depo.changeset(%Depo{}, depo) end)
  end

但是,当我运行这个函数(使用产生有效变更集的数据)时,我得到一个 NULL 列错误,因为 depo 结构中铁路系统的外键不存在。如何确保 Ecto 传递该外键?

输出:

19:06:07.401 [debug] QUERY OK db=0.8ms
begin []

19:06:07.406 [debug] QUERY OK db=0.6ms
INSERT INTO "railroad_systems" ("railroad_system_name") VALUES ($1) RETURNING "id" ["test Can insert railway system"]

19:06:07.409 [debug] QUERY ERROR db=2.7ms
INSERT INTO "depos" ("capacity","depo_name","depo_uuid") VALUES ($1,$2,$3) RETURNING "id" [23, "A depo", "d387a91b-db77-4758-87ed-9951d5c2de8a"]

19:06:07.410 [debug] QUERY OK db=0.1ms
rollback []


  1) test Can insert railway system (RailroadServer.DatabaseTest)
     apps/railroad_server/test/railroad_server/database_test.exs:9
     ** (Postgrex.Error) ERROR 23502 (not_null_violation) null value in column "railroad_system_id" violates not-null constraint

         table: depos
         column: railroad_system_id

     Failing row contains (3, null, d387a91b-db77-4758-87ed-9951d5c2de8a, A depo, 23).
     stacktrace:
       (ecto_sql) lib/ecto/adapters/sql.ex:621: Ecto.Adapters.SQL.raise_sql_call_error/1
       (ecto) lib/ecto/repo/schema.ex:649: Ecto.Repo.Schema.apply/4
       (ecto) lib/ecto/repo/schema.ex:262: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
       (ecto) lib/ecto/association.ex:927: Ecto.Association.BelongsTo.on_repo_change/5
       (ecto) lib/ecto/association.ex:413: Ecto.Association.on_repo_change/7
       (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
       (ecto) lib/ecto/association.ex:392: Ecto.Association.on_repo_change/4
       (ecto) lib/ecto/repo/schema.ex:811: Ecto.Repo.Schema.process_parents/4
       (ecto) lib/ecto/repo/schema.ex:242: anonymous fn/15 in Ecto.Repo.Schema.do_insert/4
       (ecto) lib/ecto/association.ex:662: Ecto.Association.Has.on_repo_change/5
       (ecto) lib/ecto/association.ex:432: anonymous fn/8 in Ecto.Association.on_repo_change/7
       (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
       (ecto) lib/ecto/association.ex:428: Ecto.Association.on_repo_change/7
       (elixir) lib/enum.ex:1948: Enum."-reduce/3-lists^foldl/2-0-"/3
       (ecto) lib/ecto/association.ex:392: Ecto.Association.on_repo_change/4
       (ecto) lib/ecto/repo/schema.ex:837: Ecto.Repo.Schema.process_children/5
       (ecto) lib/ecto/repo/schema.ex:914: anonymous fn/3 in Ecto.Repo.Schema.wrap_in_transaction/6
       (ecto_sql) lib/ecto/adapters/sql.ex:890: anonymous fn/3 in Ecto.Adapters.SQL.checkout_or_transaction/4
       (db_connection) lib/db_connection.ex:1415: DBConnection.run_transaction/4
       (railroad_server) lib/railroad_server/database.ex:61: RailroadServer.Database.insert_railway_system/4

版本: 长生不老药 - 1.9.0 埃克托 - 3.17 Postgrex - 0.14.3 Prosgres - 11.4

【问题讨论】:

  • lib/railroad_server/database.ex:61: RailroadServer.Database.insert_railway_system/4--那在哪里?您发布了insert_railway_system/2
  • 我删除了一些代码以简化功能。实际结构中还有两组子关联(通过两个列表传递,如 depos),具有相同的设置。但它会尝试先插入 depos 并立即失败。

标签: sql postgresql foreign-keys elixir ecto


【解决方案1】:

当我尝试使用与insert_railway_system() 中相同的代码插入时,我没有得到NULL column error。

我的架构是相似的。我的代码中唯一显着的区别在于我有约束的变更集:

|> assoc_constraint()

代替:

|> foreign_key_constraint()

但是我改变了我的代码来尝试foreign_key_constraint(),保持参数不变,相当于你的:railroad_system,插入仍然有效。当我执行相当于foreign_key_constraint(:railroad_system_id) 的操作时,插入也起作用。事实上,如果我使用foreign_key_constraint(:hello_world),插入仍然有效,据我所知,foreign_key_constraint() 的第二个参数被忽略了,这令人费解。我什至做了mix ecto.reset,它删除了存储库/数据库,重新创建了存储库/数据库,然后执行了迁移,在存储库/数据库中创建了表,我得到了相同的结果。

我的“create_depos”迁移相当于以下内容:

 add :railroad_system_id, references(:railroad_systems)

请发帖:

  1. create_depos() 函数(虽然对我来说只是使用属性映射而不是变更集也有效)

  2. 错误的完整堆栈跟踪。

  3. 您的迁移。

【讨论】:

    猜你喜欢
    • 2016-10-26
    • 1970-01-01
    • 2016-05-10
    • 2016-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-03-25
    • 1970-01-01
    相关资源
    最近更新 更多