Я нахожусь в аналогичной лодке (использую GraphQL) и решил остаться как можно дальше от cast_assoc
.Это меньше из-за сценария «создания» и больше из-за сценария «обновления».
Глядя на документацию для cast_assoc
, вы увидите, что там написано ...
- Если параметр не содержит идентификатора, данные параметра будут переданы в changeset / 2 с новой структурой и станут операцией вставки
- Если параметр содержитс идентификатором, и с таким идентификатором нет связанного дочернего элемента, данные параметров будут переданы в changeset / 2 с новой структурой и станут операцией вставки
- Если параметр содержит идентификатор и существует связанный дочерний элемент сПри таком идентификаторе данные параметров будут переданы в changeset / 2 с существующей структурой и станут операцией обновления
- Если есть связанный дочерний элемент с идентификатором, и его идентификатор не задан в качестве параметра, обратный вызов: on_replaceдля этой связи будет вызываться (см. раздел «О замене» в документации к модулю)
Сценарий 1 является вашим стандартнымчто означает, что ваши данные должны выглядеть как ваш вложенный ввод выше.(фактически это должен быть список из карт для ключа электронной почты.
Допустим, кто-то добавляет второе электронное письмо (вы указали, что это одно-много выше). Если ваш ввод выглядит как:
%{
id: 12345,
username: "test",
emails: [
%{email: "test_2@123.com"}
}
}
... это вызывает оба сценария 1 (новый параметр, без идентификатора) и сценарий 4 (дочерний элемент с идентификатором не является 't) эффективно удаляет все предыдущие электронные письма. Это означает, что ваши параметры обновления должны выглядеть примерно так:
%{
id: 12345,
username: "test",
emails: [
%{id: 1, email: "test@123.com"},
%{email: "test_2@123.com}
]
}
... что для меня означает постановку в очередь большого количества дополнительных данных в запросах.как электронные письма - которых у пользователя вряд ли будет мало - стоимость низкая. Для более широко созданной ассоциации боль -
Вместо того, чтобы всегда ставить cast_assoc
в User.changeset
одним из вариантов было бы создание определенного набора изменений для регистрации, который использует приведение только один раз:
defmodule MyApp.UserRegistration do
[...schema, regular changeset...]
def registration_changeset(params) do
%MyApp.User{}
|> MyApp.Repo.preload(:emails)
|> changeset(params)
|> cast_assoc(:emails, required: true, with: &MyApp.Email.changeset(&1, &2))
end
end
Вам все равно нужно будет указать вложенное поле emails
во входных данных, что, возможно, облом,но, по крайней мере, тогда вы не загрязняете свой обычный набор изменений пользователя cast_assoc.
Еще одна мысль: вместо того, чтобы заботиться о вложении вашего клиента, вы могли бы сделать это в своей функции распознавания, связанной с регистрацией?