Automatic update of parent record updated_at field

2019-07-15 04:34发布

问题:

Suppose I have two models: Parent and Child. If a Child record is updated, is there an option to update the associated Parent record timestamp automatically?

回答1:

There are a couple ways you can do it. One of them requires Ecto master (soon to be Ecto v2.0) and is by simply updating the parent directly:

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

Now, when the child is persisted, it will automatically propagate changes to the parent.

Alternatively, you can use Ecto v1.1 with prepare_changes and update_all to propagate the changes:

# 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

In this example, we use the prepare_changes that is executed in a transaction alongside the child changes to build a query that represents the parent model and issue an update_all updating all updated_at columns for the modules matching the query.



回答2:

You can add a function to the changeset that checks for changes and if they're valid (this is if you are updating the associations through the parent changeset).

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

You can put that at the end of your update_changeset function for the model.

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

If you wanted just to do specific associations you could do something like this:

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

:)



标签: elixir ecto