在 Ecto 中更新现有数据时,我应该使用变更集吗?

Shall I use changeset when update existing data in Ecto?

我在变更集管道中有加密令牌的功能。

def changeset(user, params \ %{}) do
  user
  |> cast(params, [:id, :token]
  |> encrypt(:token)
end

由于这个功能是新的,旧数据还没有加密,我需要手动进行。然后我遇到了问题。

如果我将原始 token 传递给变更集,它会将值视为没有变化。管道中的 encrypt 不工作并且值不更新。

如果我加密 token 并传递给变更集,它会标记为更改,并应用 encrypt 函数。但是,它加密了 twice.

一种笨拙的方法是在 encrypt 上添加检查以检查 token 是否加密。但是请记住,在我们将新令牌传递给变更集之前,我们仍然需要检查令牌是否已加密。也就是说,我们检查了两次。

所以我正在寻找一个简单的解决方案,如果有人有想法的话。

干杯

由于这是一个 运行-once 操作,我会进行迁移,直接在此处调用 Ecto.Repo.update/2 (unfortunately I did not find a way to call Ecto.Repo.update_all/3,除非您可以直接在数据库中执行 encrypt。)

defmodule My.Repo.Migrations.EncryptTokens do
  @moduledoc false
  use Ecto.Migration

  def up do
    for unencrypted <- from(...) |> Repo.all() do
      unencrypted
      |> Ecto.Changeset.change(token: encrypt(unencrypted.token))
      |> Repo.update()
    end
  end
  def down, do: raise "unrevertable"
end

旁注:你也可以看看Ecto.Changeset.force_change/3 fwiw。