为什么 Changeset.change 在 Elixir 中跳过验证?

Why Changeset.change is skipping validation in Elixir?

这是一个用于插入或更新一些数据的简单函数。 如果用户数据已经在数据库中,我只需更新它,否则我会插入一个包含数据的新行。一切正常,但我在验证时遇到问题。 变更集定义:

  def changeset(struct, params \ %{}) do
    struct
    |> cast(params, [:name, :surname, :user_id])
    |> validate_required([:name, :surname, :user_id])
    |> unique_constraint(:user_id)
  end

validate_required 目前仅在插入期间有效,在更新期间无效。

  def add_or_change(user_id, new_data) do
    data_from_db = data_by_user_id (user_id)
    case data_from_db do
      nil ->
        Data.changeset(%Data{}, new_data)
        |> Repo.insert()

      _ ->
        Changeset.change(data_from_db, new_data)
        |> Repo.update()
    end
  end

如果我尝试将“”作为 :name 值插入,我会按预期收到错误消息(不能为空)。但是,如果我使用“”更新现有行作为 :name 值,变更集不会通过验证并且我的数据库更新不正确。如何在 Repo.update() 之前强制对更改进行验证??

根据文档:Ecto.Changeset/2 用于内部数据更改,因此它绕过了验证:

The function is meant for working with data internal to the application. Because of that neither validation nor casting is performed. This means change/2 expects the keys in the changes map or keyword to be atoms.

您应该使用 Ecto.Changeset.cast/4 来应用验证,如果有效则更新。

不要使用这个:

    Changeset.change(data_from_db, new_data)

只是 运行 您已经在使用的相同功能:

    Data.changeset(data_from_db, new_data)

顺便说一句,你其实可以把这个函数简化很多:

  def add_or_change(user_id, new_data) do
    (data_by_user_id(user_id) || %Data{})
    |> Data.changeset(new_data)
    |> Repo.insert_or_update()
  end