You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Ecto插入冲突时无法重置deleted_at为nil的技术求助

解决方案:在Ecto插入冲突时重置deleted_at为nil

我明白你的需求:当插入用户时遇到邮箱冲突(因为邮箱是唯一约束),希望把现有记录的deleted_at字段强制重置为nil,不管它之前的值是什么,而且这个字段可能没在你的changeset里传递或被允许。

问题根源

你之前用on_conflict: :replace_all的方式,只会更新changeset中包含的字段。如果deleted_at不在changeset的字段列表里(比如你的changeset没有允许这个字段,或者你没在attrs里传递它),Ecto就不会去更新这个字段。所以我们需要明确指定要更新的字段,强制把deleted_at设为nil。

方法1:自定义on_conflict的set语句

这种方式最直接,明确列出要更新的字段,同时强制设置deleted_at: nil:

# 不需要在attrs里传deleted_at,我们直接在on_conflict里设置
attrs = %{"email" => "tanweer.shahzaad@gmail.com"}
cs = %User{} |> User.changeset(attrs)

ExApi.Repo.insert(cs, 
  on_conflict: [
    set: [
      # 保留新生成的邮箱确认令牌(你的schema默认会生成这个值)
      email_confirmation_token: excluded.email_confirmation_token,
      # 强制重置deleted_at为nil
      deleted_at: nil,
      # 手动更新updated_at,或依赖Ecto自动处理(取决于你的timestamps配置)
      updated_at: DateTime.utc_now()
    ]
  ],
  conflict_target: :email
)

这里的excluded是Ecto提供的特殊变量,代表原本要插入但冲突了的那条记录的字段值。用它来获取新生成的email_confirmation_token,就能把新的令牌更新到现有记录上。

方法2:结合replace_all_except和额外设置

如果你希望保留changeset里所有字段的更新,同时只强制重置deleted_at,可以用replace_all_except排除deleted_at,再用set覆盖它:

attrs = %{"email" => "tanweer.shahzaad@gmail.com"}
cs = %User{} |> User.changeset(attrs)

ExApi.Repo.insert(cs, 
  on_conflict: [
    # 替换changeset里除了deleted_at之外的所有字段
    :replace_all_except, [:deleted_at],
    # 强制把deleted_at设为nil
    set: [deleted_at: nil]
  ],
  conflict_target: :email
)

额外注意:确保Changeset允许必要字段

如果你的User.changeset没有允许email_confirmation_token等字段,那即使你在on_conflict里用excluded,这些字段也不会被正确传递。所以你的User.changeset应该包含这些字段的cast操作:

def changeset(user, attrs) do
  user
  |> cast(attrs, [:email, :email_confirmation_token, :email_confirmed_at, :deleted_at])
  |> validate_required([:email])
  # 其他验证逻辑...
end

如果不想让外部修改deleted_at,你可以在changeset里添加条件验证,或者直接在on_conflict里强制设置——因为on_conflict的set是直接操作数据库的,不受changeset字段权限限制。

内容的提问来源于stack exchange,提问作者Radio Active

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 07:15:12