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
:)