Hello, I have the below schema relationship network (the names are for illustrative purposes):
| --- Revision |
->| active: false |
--/ | --- |
--/ +---------------+
+--------------------------------+ --/ +---------------+
| --- Book | --/ | --- Revision |
| has_one where: active == true |-/------------->| active: false |
| --- | -\ | --- |
+--------------------------------+ --\ +---------------+
---\ +---------------+
--\ | --- Revision |
->| active: true |
| --- |
+---------------+
It’s a one-to-many relationship where one of the Revisions has a property called active
that is set to true
and the rest are set to false
. The Revision where active is set to true represents the “current version” of the Book. I am having trouble getting a specific action working in Ecto due to how cast_assoc
appears to work. Instead of modifying the active revision I want it to create with a new Revision that is active with the same properties (plus the modifications):
- Set current Revision’s active property to false
- Copy the current Revision attributes to a new Revision
- Set the new Revision’s active property to true
- Apply any changes being made to the old Revision instead to the new Revision
The reason for this approach is that we want an immutable history of Revision records while maintaining a simplified interface for the user (they only see the most recent Revision).
In my initial approach of this process I attempted to change the current active revision relationship via cast_assoc however using that complained because on_replace
defaults to raise, but didn’t find an on_replace
option that satisfies my needs. Instead I have to bifurcate the changeset logic in a way that one casts the assoc for validation purposes and the other simply ignores the casting (so that I can change the relationship in another way).
Currently I made an Ecto.Multi
chain that will: Set all previous revisions active to false; create a new revision with active true; signal the update changeset on the parent to avoid casting the child (to allow updating the other parent attributes).
I feel like this is cumbersome and not elegant at all. The most problematic bit being identifying whether any changes to the current active child happened or not and forking the logic.
I wonder if it would be possible to have a customization of the on_replace
behavior where I can describe a process where I want to duplicate and insert a new record, associate it, and keep the old record around.
Maybe there is a better way to approach this that I missed?
Thank you