我有一个简单的before_save转换,并了解到phoenix使用Ecto changest来执行此任务。
我的Stage
模型有一个position
属性,默认为current maximum + 1
,因此尝试按如下方式实现:
舞台模型:
def changeset(struct, params \\ %{}) do
struct
|> cast(params, @required_fields, @optional_fields)
|> validate_required([:name])
|> set_position
end
defp set_position(current_changeset) do
# get current max position from db
max_position = Repo.one(
from s in Stage,
select: fragment("COALESCE(MAX(?),0)", s.position)
)
case current_changeset do
%Ecto.Changeset{valid?: true} ->
put_change(current_changeset, :position, max_position+1)
_ ->
current_changeset
end
end
在逐个插入记录时工作正常但在批量插入时失败;例如,在seed
下面的文件中。
种子
alias MyApp.{Repo, Post}
[
%{name: "Requirements"},
%{name: "Quotation"},
%{name: "Development"},
%{name: "Closing"}
]
|> Enum.map(&Post.changeset(%Post{}, &1))
|> Enum.each(&Repo.insert!(&1))
预期/当前行为:
如果当前最高位置为7
,则对于上面所有插入的4条记录,位置将分别设置为8
而不是8,9,10,11
!那是因为第一个管道将准备所有的更改然后插入它们!
我播种的方式错了吗?还是变革集?我怎样才能重新设计这个,所以无论我如何进行插入,行为都是一样的?任何改善我如何做的反馈表示赞赏!
答案 0 :(得分:5)
您可以使用Ecto.Changeset.prepare_changes/2
在该变更集的数据库事务中运行任意计算。您的set_position/1
函数具有正确的参数/返回值(changeset - > changeset),因此您只需要更改:
|> set_position
到
|> prepare_changes(&set_position/1)
和set_position
现在将在插入Post的同一事务中执行,而不是在创建变更集时执行。