我有以下情况:
我将产品及其变体(has_many
)存储在数据库中。然后我在一个请求中将Product及其Variants发送到API:
payload = %Product{
# ...
variants: [
%Variant{
# ...
option1: "Black",
option2: "64GB",
},
%Variant{
# ...
option1: "Silver",
option2: "64GB",
}
],
vendor: "Apple"
}
当我得到回复时,我需要在我的数据库中更新产品及其变体。响应:
%Shopify.Product{
# ...
created_at: "2018-04-26T12:54:33-04:00",
variants: [
%Shopify.Variant{
# ...
created_at: "2018-04-26T12:54:33-04:00",
option1: "Black",
option2: "64GB",
title: "Black / 64GB",
},
%Shopify.Variant{
# ...
created_at: "2018-04-26T12:54:33-04:00",
option1: "Silver",
option2: "64GB",
title: "Silver / 64GB",
}
],
}
当我尝试使用cast_assoc
更新关联的变体时,ecto会删除我在数据库中的变体记录,并从有效负载中插入相同的变体记录。
我知道那是因为on_replace: :delete
,但如果我没有设置:on_replace
,Ecto会抱怨它不知道如何替换。但为什么要替换?
我需要更新相关记录。我尝试将数据库中的变量id注入到响应中,我将其用作更新的属性,因此Ecto可以将响应中的变体与数据库中的变体链接起来。
全天与此战斗。甚至可以更新相关记录吗?我错过了什么?
这是我的架构:
defmodule Product do
# ...
has_many(:variants, Variant, on_replace: :delete)
# ...
def changeset_from_response(product, response) do
attrs =
response
|> Utils.map_from_struct_deep()
|> put_variant_ids(product)
product
|> cast(attrs, @castable_attrs)
# ...
# HERE **********
|> cast_assoc(:variants, with: &Variant.changeset_from_response/2)
# HERE **********
end
end
defmodule Variant do
# ...
belongs_to(:product, Product)
# ...
def changeset_from_response(variant, response) do
attrs =
response
|> Utils.map_from_struct_deep()
variant
|> cast(attrs, @castable_attrs ++ [:id, :product_id])
# ...
end
end
应该完成工作的简化代码:
product = Repo.get(Product, id) |> Repo.preload(:variants)
payload = # product to payload
shopify_product = Shopify.Product.create(payload)
changeset = Product.changeset_from_response(product, shopify_product)
iex> changeset
#Ecto.Changeset<
action: nil,
changes: %{
# ...
shopify_created_at: ~N[2018-04-26 12:54:33],
shopify_id: "1333797453908",
variants: [
#Ecto.Changeset<action: :replace, changes: %{}, errors: [],
data: #ProdSync.Data.Variant<>, valid?: true>,
#Ecto.Changeset<action: :replace, changes: %{}, errors: [],
data: #ProdSync.Data.Variant<>, valid?: true>,
***** HERE IT DELETES VARIANTS AND INSERTS NEW ONES ********
#Ecto.Changeset<
action: :insert,
changes: %{
id: 26,
option1: "Black",
option2: "64GB",
title: "Black / 64GB",
},
errors: [],
data: #ProdSync.Data.Variant<>,
valid?: true
>,
#Ecto.Changeset<
action: :insert,
changes: %{
id: 27,
option1: "Silver",
option2: "64GB",
title: "Silver / 64GB",
},
errors: [],
data: #ProdSync.Data.Variant<>,
valid?: true
>,
]
},
errors: [],
data: #ProdSync.Data.Product<>,
valid?: true
>
答案 0 :(得分:0)
原来cast_assoc
在这里:
|> cast_assoc(:variants, with: &Variant.changeset_from_response/2)
将现有变体与属性变体匹配,以便能够调用Variant.changeset_from_response(variant, attrs)
如果您在id
中没有正确的变体attrs
,则会认为attrs
用于创建新记录。
我在id
内向attrs
添加了Variant.changeset_from_response/2
,为时已晚。