我在这里阅读了cast_assoc/3的内容。但是文档看起来很混乱。我想构建single changeset for the entire association
并在一个事务中执行它以进行更新。这是我的模特
;
defmodule User do
use Gallery.Web, :model
schema "users" do
field(:name, :string)
field(:occupation, :string)
has_many(:paintings, Painting)
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [ :name, :occupation ])
|> validate_required([:name, :occupation])
end
end
defmodule Painting do
use Gallery.Web, :model
schema "paintings" do
field(:name, :string)
belongs_to(:users, User)
end
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [ :name ])
|> validate_required([:name])
end
end
这是我要构建单个变更集的数据
data= %User{
__meta__: #Ecto.Schema.Metadata<:loaded, "users">,
id: 4606,
name: "Test",
occupation: "Artist",
paintings: [
%Painting{
__meta__: #Ecto.Schema.Metadata<:loaded, "paintings">,
user_id: 4606,
id: 1515,
name: "philip"
},
%Painting{
__meta__: #Ecto.Schema.Metadata<:loaded, "paintings">,
user_id: 4606,
id: 1516,
name: "john"
}
]
}
有什么建议吗?
谢谢
答案 0 :(得分:1)
要使变更集起作用,您的数据需要是纯映射而不是结构(就像从params中获取的那样)。
如果您只想插入具有多幅画的用户,则需要:
cast_assoc
赞:
data = %{
name: "Test",
occupation: "Artist",
paintings: [
%{
name: "philip"
},
%{
name: "john"
}
]
}
%User{}
|> User.changeset(data)
|> Repo.insert
如果您还想以这种方式更新内容,它将变得更加复杂。目前尚不清楚data
中的绘画列表是否应该更新现有的绘画,添加新的绘画或删除所有先前的绘画并将其替换为data
中的绘画。我个人不建议使用嵌套的变更集进行更新。 https://hexdocs.pm/ecto/Ecto.Changeset.html#cast_assoc/3
澄清后更新:
要更新所有的绘画,您还需要做两件事。您需要:
赞:
data = %{
name: "Test",
occupation: "Artist",
paintings: [
%{
id: 1,
name: "philip"
},
%{
id: 2,
name: "john"
}
]
}
User
|> Repo.get_by(id: user_id)
|> Repo.preload(:paintings)
|> User.changeset(data)
|> Repo.update
您不需要使用Multi
。这将是一次交易。一次使用Repo
模块通常表示一个数据库操作。
所有魔力发生在paintings: [...]
中。根据文档,您有四种情况:
- 如果参数不包含ID,则参数数据将使用新结构传递到changeset / 2并成为插入操作
- 如果参数包含一个ID,并且没有与此ID关联的子代,则参数数据将通过一个 新结构并成为插入操作
- 如果参数包含一个ID并且有一个与此ID关联的子代,则参数数据将通过 现有结构并成为更新操作
- 如果存在一个具有ID的关联子代,并且未将其ID作为参数给出,则该关联的:on_replace回调将为 调用(请参阅模块文档中的“替换时”部分)
如果您有第三种情况需要更新,您会感兴趣。如果您没有通过data
中的所有绘画,那么您可能还会对第四幅绘画感兴趣。