Как использовать add_error для вложенного набора изменений?

#elixir #ecto

Вопрос:

Из того, что я понимаю в документации по add_error, это может относиться только к набору изменений верхнего уровня.

Однако как я могу использовать add_error для вложенного набора изменений?

Например, возьмем эти схемы:

 defmodule MyApp.Person do
  use Ecto.Schema
  import Ecto.Changeset

  schema "people" do
    field :first_name, :string
    field :last_name, :string
    field :other_field_made_to_fail, :string

    has_many :addresses, MyApp.Address
  end

  def changeset(person, attrs) do
    person
    |> cast(attrs, [:first_name, :last_name, :other_field_made_to_fail])
    |> validate_required([:first_name, :last_name])
  end
end

defmodule MyApp.Address do
  use Ecto.Schema
  import Ecto.Changeset

  schema "addresses" do
    field :street_name, :string
    field :city, :string
    field :state, :string

    belongs_to :person, MyApp.Person
  end

  def changeset(address, attrs) do
    address
    |> cast(attrs, [:street_name, :city, :state])
    |> validate_required([:city, :state])
    |> cast_assoc(:person, with: amp;MyApp.Person.changeset/2, required: true)
  end
end
 

И возьмите этот ввод данных:

 %{
  "address" => %{
    "city" => "Test City",
    "state" => "Test State",
    "person" => %{
      "first_name" => "John",
      "last_name" => "Doe"
    }
  }
}
 

И этот результирующий набор изменений:

 #Ecto.Changeset<
  changes: %{
    address: #Ecto.Changeset<
      changes: %{
        city: "Test City",
        state: "Test State"
        person: #Ecto.Changeset<
          changes: %{
            first_name: "John",
            last_name: "Doe"
          }
        >
      },
      errors: []
      valid?: true
    >
  }
>
 

Как бы я использовал add_error/4 , чтобы добавить ошибку :other_field_made_to_fail внутрь вложенного :person атрибута и сохранить оставшуюся часть набора изменений нетронутой?

Комментарии:

1. Зачем вам вообще понадобилось обрабатывать это с помощью самой внешней схемы? Ошибка должна быть добавлена во MyApp.Person.changeset/2 время проверки Person , чтобы она распространялась cast_assoc/2 и могла быть обработана Ecto.Changeset.traverse_errors/2 позже.

2. @AlekseiMatiushkin, потому :other_field_made_to_fail что это не является частью пользовательского ввода. Я хотел вызвать ошибку в этом поле на основе остальной части ввода пользователя. Это означает, что мне нужно вмешаться в набор изменений вместо того, чтобы позволить Ecto идти своим чередом.

Ответ №1:

Мне удалось это выяснить. В случае, если есть другие заинтересованные люди, решение состоит в том, чтобы вызвать update_change/3 в вашем наборе изменений и вызвать функцию в качестве 3-го аргумента, представляющего ваш вложенный набор изменений:

 Ecto.Changeset.update_change(changeset, :person, fn person_changeset ->
  Ecto.Changeset.add_error(person_changeset, :other_field_made_to_fail, "error message here")
end)
 

Конечным результатом является обновленный Address набор изменений, который теперь включает Person набор изменений с ошибкой в нем.

Вы также можете использовать update_change/3 для выполнения других функций набора изменений во вложенных наборах изменений, и он вернет обновленный вложенный набор изменений.