如何将 Ecto 变更集设计为真正的 before_save 回调
How to design an Ecto changeset as a true before_save callback
我进行了简单的 before_save 转换,了解到 phoenix 使用 Ecto changest 完成此任务。
我的 Stage
模型有一个默认为 current maximum + 1
的 position
属性,因此尝试按如下方式实现:
舞台模特:
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))
Expected/Current 行为:
如果当前最大位置是 7
,对于上面所有插入的 4 条记录,位置将分别设置为 8
而不是 8,9,10,11
!那是因为第一个管道将准备所有变更然后插入它们!
是我播种方式不对吗?还是变更集?我如何重新设计它,以便无论我如何插入,行为都是相同的?感谢任何改进我的工作方式的反馈!
您可以在该变更集的数据库事务中使用 Ecto.Changeset.prepare_changes/2
到 运行 任意计算。您的 set_position/1
函数具有正确的 argument/return 值(变更集 -> 变更集),因此您只需更改:
|> set_position
到
|> prepare_changes(&set_position/1)
和 set_position
现在将在插入 Post 之前在同一事务中执行,而不是在创建变更集时执行。
我进行了简单的 before_save 转换,了解到 phoenix 使用 Ecto changest 完成此任务。
我的 Stage
模型有一个默认为 current maximum + 1
的 position
属性,因此尝试按如下方式实现:
舞台模特:
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))
Expected/Current 行为:
如果当前最大位置是 7
,对于上面所有插入的 4 条记录,位置将分别设置为 8
而不是 8,9,10,11
!那是因为第一个管道将准备所有变更然后插入它们!
是我播种方式不对吗?还是变更集?我如何重新设计它,以便无论我如何插入,行为都是相同的?感谢任何改进我的工作方式的反馈!
您可以在该变更集的数据库事务中使用 Ecto.Changeset.prepare_changes/2
到 运行 任意计算。您的 set_position/1
函数具有正确的 argument/return 值(变更集 -> 变更集),因此您只需更改:
|> set_position
到
|> prepare_changes(&set_position/1)
和 set_position
现在将在插入 Post 之前在同一事务中执行,而不是在创建变更集时执行。