A while ago I wrote a little quiz for our staff so people could see how good they are at recognizing their co-workers. At some point, we started getting some rollbar errors from it that looked something like this:
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: update or delete on table "users" violates foreign key constraint...
That seemed very odd. My little quiz wasn't supposed to try deleting users from the database! I tracked it down to code that looked something like this.
similar_users = correct_user.department.users similar_users.delete(correct_user)
The point was that we had a
correct_user, i.e. the user whose photo the quiz taker should pick from a group of photos, and we wanted to find other users from the same department, to increase the likelihood that we might stump the quiz taker. So to that end:
- Get an array of all the users who are in the same department as the
- Remove the
correct_userfrom the array.
At some point, this actually worked. But when the exception starting occurring, this was what was happening instead:
- Get an
ActiveRecord::Relationobject that is capable of getting all of the users from that department out of the database.
- Call delete on that
ActiveRecord::Relationobject, passing in the
correct_user, which will then attempt to delete that user from the database!
So at some point (maybe in the move from Rails 3 to Rails 4) the object that was returned by
correct_user.department.users was changed out from under that bit of code, with potentially disastrous results. Thank heavens for foreign key constraints!
How can we make the code better? Well we could force it to return an array:
similar_users = correct_user.department.users.to_a similar_users.delete(correct_user)
But it would probably be better to avoid the destructive (in either case) delete method altogether:
similar_users = correct_user.department.users - [correct_user]
Better and cleaner.