Пожалуйста, почему я теряю данные об ассоциациях детей при сбое вставки ecto changeset?
Таким образом, у меня есть схема клиента, которая принадлежит схеме пользователя и имеет одну схему компании (необязательно).
Вот набор изменений клиента, который я использую с формой регистрации:
@doc false
def create_changeset(%Client{} = client, attrs) do
client
|> cast(attrs, [:qualification, :phone])
|> cast_assoc(:user, with: &User.create_changeset/2)
|> cast_assoc(:company)
|> validate_required([:qualification])
|> unique_constraint(:user_id)
end
А теперь вот контекстная функция, которая создает пользователя, клиента и компанию одновременно.
def create_client(attrs \\ %{}) do
%Client{}
|> Client.create_changeset(attrs)
|> Repo.insert()
end
Когда все необходимые данные и их проверка в форме регистрации в порядке, вставка завершается успешно, и все идет, как я надеюсь. Даже если проверка формы завершается неудачно перед попыткой вставки, форма регистрации возвращается со всеми ошибками и заполняется ранее отправленными данными...
Но когда вставка завершается неудачей в ограничении базы данных пользователя (например, уже принятом электронном письме), форма возвращается со всеми данными и ошибками только для схемы пользователя и клиента. Все данные компании утеряны. Это заставит клиента повторно ввести всю информацию о своей компании. Это поведение неизбежно?
В случае, если это может быть полезно, я использую библиотеку Phauxth для аутентификации пользователя.
2 ответа
Если вы хотите создать компанию независимо от клиента, рассмотрите возможность использования альтернатив для cast_assoc.
Я не знаю точно ваш вариант использования, но похоже, что вы хотите создать компанию, если с данными клиента все в порядке и независимо от пользовательских данных. Может быть, вы можете позвонить каждому changeset
для каждой схемы явно, не приводя ее в Client.changeset
,
Кроме того, ваш клиент не должен отправлять вам снова attr
, как у вас уже есть - даже если Repo.insert
не удается, у вас есть доступ к changeset.params
, так что вы можете использовать их с некоторой дополнительной логикой.
Вместо того, чтобы добавлять каждый параметр вручную, вы можете объединить свои ошибки набора изменений с набором изменений до выполнения операции с базой данных:
def create(conn, %{"client" => client}) do
changes = Client.changeset(%Client{}, client)
case Repo.insert(changes) do
{:ok, _} -> ...
{:error, errors} ->
changes = Ecto.Changeset.merge(changes, errors)
render(conn, "new.html", changes: changes)
end
end
Обратите внимание, что порядок изменений имеет значение. Второй набор изменений имеет приоритет над первым 1, поэтому ошибки переопределят все существующие поля.
1 https://hexdocs.pm/ecto/Ecto.Changeset.html#merge/2