自动更新父记录updated_at字段(Elixir-Ecto)

时间:2016-01-07 12:12:01

标签: elixir ecto

假设我有两个模型:父母和孩子。 如果更新了子记录,是否可以选择自动更新关联的父记录时间戳?

2 个答案:

答案 0 :(得分:4)

有几种方法可以做到。其中一个需要Ecto master(很快就会成为Ecto v2.0),只需直接更新父代:

# Assuming a child_changeset with the parent loaded
child_changset = Ecto.Changeset.put_assoc(child_changeset, :parent, %{updated_at: Ecto.DateTime.utc})

现在,当孩子被持久化时,它会自动将更改传播到父级。

或者,您可以将Ecto v1.1与prepare_changes和update_all一起使用来传播更改:

# Assuming a child_changeset
Ecto.Changeset.prepare_changes child_changeset, fn changeset ->
  query = Ecto.assoc(changeset.model, :parent)
  changeset.repo.update_all query, set: [updated_at: ^Ecto.DateTime.utc]
  changeset
end

在此示例中,我们使用在事务中执行的prepare_changes以及子更改来构建表示父模型的查询,并为匹配查询的模块发出update_all更新所有updated_at列。

答案 1 :(得分:1)

您可以向变更集添加一个函数,用于检查更改以及它们是否有效(这是通过父变更集更新关联)。

def touch_parent(%{changes: changes, valid: true}=changeset) when map_size(changes) > 0 do
  updated_at = DateTime.utc_now() |> DateTime.to_naive()
  cast(changeset, %{updated_at: updated_at}, [:updated_at])
end
def touch_parent(changeset), do: changeset

您可以将其放在模型的update_changeset函数的末尾。

def update_changeset(%Model{}=model, attrs) do
  model
  # casting
  |> touch_parent()
end

如果您只想做特定的关联,您可以这样做:

def touch_parent(%{changes: changes, valid: true) when map_size(changes) > 0 do
  updated_at = DateTime.utc_now() |> DateTime.to_naive()
  changed_fields = Map.keys(changes)
  cond do
    :association in changed_fields ->
      cast(changeset, %{updated_at: updated_at}, [:updated_at])
    true -> changeset
  end
end

:)