I’m trying to delete an association from a record by setting its value to nil in my Changeset. This all seems to work properly, but I get an error during the transaction and it rolls everything back:
That error happens when an UPDATE updates zero rows when at least one was expected.
That doesn’t make this any less confusing, though. The only way I could see that UPDATE "elements" statement doing that would be if the target elements row stopped existing…
Just from the sql logs, my bet (and i’m not sure), you’re regenerating the id from the association, so what’s happening is
you had record with one id
you try to update it, but then also regenerate od
on commit, because the id is missing, it does a delete
then it tries to do an update on the record with the different id (it has an id, so it thinks it needs to be an update) and fails, because the record doesn’t exist
Not sure how well explained that is, but hopefully it helps and doesn’t send you on the wrong trail.
Hrm, so is there a different way I should handle this update? I was hoping the association’s on_replace: delete would handle the heavy-lifting for me. I know I could switch to doing this more manually- if the association is new, nil it out, if it’s existing, set the action: delete on the child data.
That would work for replacements, but I have this same error for deletes.
To me the cleanest way to update this association is to nil it out, then possibly put something new in later. That lets my form be very dumb: it just looks to see if there’s a value. The other solutions (eg action: delete) will force me to handle the edge cases in my heex, handle_event, and changeset.
Can you check the configuration of the elements table, in particular any foreign keys? The behavior you’re observing could be caused by an ON DELETE CASCADE.
A foreign key constraint on elements.schedule_id with ON DELETE CASCADE means that the rows in elements will be deleted when the corresponding row in schedules is deleted, not the other way around.
This explains the error you’re seeing: the DB is deleting the Element out from under you!
A more-typical way to write this association (especially if the element <-> schedule relation might include multiple schedules someday) is with the belongs_to on the Schedule side and a has_one in Element.
That was it! I thought that :delete_all worked the other way. I changed it to :nothing but then started getting a foreign key constraint error. :nilify_all was what I actually wanted.