acts_as_list: Cannot add position uniqueness index
In order to fight against the duplicated position issue the document suggests to add position uniqueness index. However this constraint forbids the position being updated. Positions are adjusted in the after_update method updated_positions
. Specifically it fixes duplicates when they are found in https://github.com/brendon/acts_as_list/blob/master/lib/acts_as_list/active_record/acts/list.rb#L418
In my test this updated_positions
has no chance to be called because the uniqueness constraints already raises ActiveRecord::RecordNotUnique Exception: PG::UniqueViolation
About this issue
- Original URL
- State: open
- Created 4 years ago
- Reactions: 6
- Comments: 16 (7 by maintainers)
Commits related to this issue
- Use a deferrable constraint to allow position changes https://github.com/brendon/acts_as_list/issues/378 — committed to moveson/edify by moveson 2 years ago
Hey @brendon – yeah, I’ll work on putting something together. I figured moving the callback would probably be too big of a change. It works as-is, though I do think it would be good to be able to enforce the uniqueness constraint at the DB level.
I’m running into this issue with uniqueness indices configured on Postgres, regardless of the
:sequential_updates
setting. I removed the indices for now. A call toitem.move_higher
will fail when the index is present. It works once I have removed it.As @bzwei noted, I think this is due to the
after_update
callback. The record can’t save because it violates the unique constraint before the otherposition
values are updated. I’d have to dig into the code a bit more, but using a before_save callback could work, but could also have unintended consequences. For now, perhaps a note on the documentation would help.This should help:
https://www.postgresql.org/docs/current/sql-set-constraints.html
There might be some implications: Rails automatically wraps any test case in a database transaction that is rolled back after the test completes (see https://guides.rubyonrails.org/testing.html#testing-parallel-transactions ). So your test might behave differently than your production code (constraint won’t be checked in tests while it is checked in development/production) if you don’t use transactions. In tests you could use self.use_transactional_tests = false and later clean up your test database if your constraint is initially deferred.
But I didn’t have a look if this is an issue here. Maybe everything is wrapped in a transaction. Then there shouldn’t be a problem