【问题标题】:Ecto: Insert changeset with foreign_key_constraint raise Postgrex.ErrorEcto:使用 foreign_key_constraint 插入变更集引发 Postgrex.Error
【发布时间】:2023-03-20 10:49:01
【问题描述】:

我正在使用 Ecto (2.2.8) 来处理现有的 PostgreSQL 数据库。

我定义了两个模式来表示ownerhousehouse 架构有一个 FK (belongs_to) owner。我为house 定义了一个架构和一个变更集,如下所示:

 @primary_key {:id, :id, autogenerate: true}
  schema "house" do
    belongs_to :owner, Owner, foreign_key: :owner_id
    field :name, :string
  end

  def changeset(house, params \\ %{}) do
    house
    |> cast(params, [:name, :owner_id])
    |> validate_required([:owner_id])
    |> foreign_key_constraint(:owner_id)
  end

问题

当数据库中没有带有id 10owner 记录时,我希望以下代码返回一个元组{:error, changeset}

House.changeset(%House{}, %{name: "Whatever",  owner_id: 10}) |> Repo.insert

但是,我收到 Postgrex.Error

** (Postgrex.Error) ERROR 23503 (foreign_key_violation): insert or update on table "house" violates foreign key constraint "house_owner_id_fkey".

如果我在调用Repo.insert 之前检查changeset.constraints 的内容,这就是我得到的:

House.changeset(%House{}, %{name: "Whatever",  owner_id: 10}) 
|> Map.get(:constraints)

[
  %{
    constraint: "house_owner_id_fkey",
    error: {"does not exist", []},
    field: :owner_id,
    match: :exact,
    type: :foreign_key
  }
]

那么,当使用 foreign_key_constraint 时,我不应该得到一个元组 {:error, changeset},我可以按照文档的建议进行模式匹配吗?

更新

我没有使用 Ecto 的迁移。数据库表已经创建,迁移由不同的项目处理。 SQL Schema 如下所示:

    Column     |  Type   |                       Modifiers
---------------+---------+-------------------------------------------------------
 id            | integer | not null default nextval('house_id_seq'::regclass)
 owner_id      | integer | not null
Indexes:
    "house_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
    "house_owner_id_fkey" FOREIGN KEY (owner_id) REFERENCES owner(id) DEFERRABLE INITIALLY DEFERRED

【问题讨论】:

    标签: postgresql elixir ecto


    【解决方案1】:

    这很奇怪。我有一个非常相似的方案,它在相同的约束条件下按预期工作。

    defmodule Hello.Accounts.Credential do
      use Ecto.Schema
      import Ecto.Changeset
      alias Hello.Accounts.{Credential, User}
    
    
      schema "credentials" do
        field :email, :string
        belongs_to :user, User, foreign_key: user_id
    
        timestamps()
      end
    
      @doc false
      def changeset(%Credential{} = credential, attrs) do
        credential
        |> cast(attrs, [:email, :user_id])
        |> validate_required([:email, :user_id])
        |> unique_constraint(:email)
        |> foreign_key_constraint(:user_id)
      end
    end
    

    当我插入一条无效记录(不存在的user_id)时:

    Credential.changeset(%Credential{}, %{email: "test@c.c", user_id: 2}) |> Repo.insert
    
        {:error,
         #Ecto.Changeset<action: :insert, changes: %{email: "test@c.c", user_id: 2},
          errors: [user_id: {"does not exist", []}], data: #Hello.Accounts.Credential<>,
          valid?: false>}
    

    但是,之前出现了相同的错误,但我认为在我使用此任务重置数据库时它已修复:

    mix ecto.drop
    mix ecto.create
    mix ecto.migrate 
    mix run priv/repo/seeds.exs
    

    该任务重新创建数据库并运行您的迁移。

    凭据 SQL 架构:

                                          Table "public.credentials"
       Column    |            Type             |                        Modifiers
    -------------+-----------------------------+----------------------------------------------------------
     id          | bigint                      | not null default nextval('credentials_id_seq'::regclass)
     email       | character varying(255)      |
     user_id     | bigint                      | not null
     inserted_at | timestamp without time zone | not null
     updated_at  | timestamp without time zone | not null
    Indexes:
        "credentials_pkey" PRIMARY KEY, btree (id)
        "credentials_email_index" UNIQUE, btree (email)
        "credentials_user_id_index" btree (user_id)
    Foreign-key constraints:
        "credentials_user_id_fkey" FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
    

    项目数据库依赖项:

    Phoenix-Ecto -> 3.2 Postgresx >= 0.0.0

    【讨论】:

    • 是的,这与我的代码非常相似。数据库迁移由不同的项目控制,所以我没有定义任何 Ecto 迁移。你能告诉我凭据的 SQL 架构吗?
    • 另外,请告诉我你的 Ecto 版本是什么?
    • 我正在使用使用 ecto 2.1 的 phoenix-ecto ({:phoenix_ecto, "~> 3.2"})
    猜你喜欢
    • 1970-01-01
    • 2016-11-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多