Ecto更新时Changeset函数未触发,如何设置仅更新执行的函数?
Ecto更新时触发自定义逻辑的解决方案
你遇到的问题根源在于:Ecto.Changeset.change/2是Ecto原生的基础函数,只会生成最简化的changeset,不会调用你自定义的User.changeset/2这类包含校验、哈希逻辑的函数。要实现更新时触发特定逻辑(比如密码哈希),可以通过以下两种方式解决:
1. 在通用changeset中区分插入/更新场景
在自定义的changeset函数里,用Ecto.Changeset.persisted?/1判断当前记录是否已持久化(即是否是更新场景),针对该场景添加专属逻辑。
示例代码(密码哈希场景):
defmodule Api.User do use Ecto.Schema import Ecto.Changeset schema "users" do field :email, :string field :username, :string field :password_hash, :string # 虚拟字段,用于接收明文密码 field :password, :string, virtual: true field :password_confirmation, :string, virtual: true end def changeset(user, attrs) do user |> cast(attrs, [:email, :username, :password, :password_confirmation]) |> validate_required([:email, :username]) |> validate_confirmation(:password) # 仅在更新场景触发的逻辑:如果是已持久化记录且密码字段有值,执行哈希 |> maybe_hash_password() end defp maybe_hash_password(changeset) do if persisted?(changeset) and get_change(changeset, :password) do changeset |> put_change(:password_hash, Bcrypt.hash_pwd_salt(get_change(changeset, :password))) else changeset end end end
更新时直接调用这个自定义changeset函数,而不是Ecto.Changeset.change/2:
iex> user = Repo.get_by(User, email: "email") iex> changeset = User.changeset(user, %{password: "password", password_confirmation: "password"}) iex> Repo.update(changeset)
2. 编写专属的更新用changeset函数
如果更新场景的逻辑和插入差异较大,可以单独定义一个update_changeset/2函数,仅处理更新时的需求:
defmodule Api.User do # ... 其他代码省略 ... def update_changeset(user, attrs) do user |> cast(attrs, [:password, :password_confirmation]) |> validate_confirmation(:password) |> hash_password_if_present() end defp hash_password_if_present(changeset) do case get_change(changeset, :password) do nil -> changeset password -> put_change(changeset, :password_hash, Bcrypt.hash_pwd_salt(password)) end end end
更新时调用这个专属函数:
iex> user = Repo.get_by(User, email: "email") iex> changeset = User.update_changeset(user, %{password: "password", password_confirmation: "password"}) iex> Repo.update(changeset)
关键注意点
- 永远不要直接用
Ecto.Changeset.change/2来处理需要自定义逻辑的更新操作,它不会触发任何你定义的校验或转换逻辑。 - 用
persisted?/1判断更新场景是最可靠的方式,它会检查记录是否已经被持久化到数据库中。
内容的提问来源于stack exchange,提问作者Basil
相关产品推荐
相关产品推荐

