问题
First, let me set the stage:
I have an app_settings table that joins app_setting_options with user_id, user_role_id, event_id, group_id, or app_level_setting. For any given app_setting there should be a specific app_setting_option_id and only one of those other objects. For each one of those secondary objects, I have specified a uniqueness validation with a scope in the AppSetting model:
validates :user_id, uniqueness: { scope: :app_setting_option_id}, allow_nil: true
validates :user_role_id, uniqueness: { scope: :app_setting_option_id}, allow_nil: true
validates :group_id, uniqueness: { scope: :app_setting_option_id}, allow_nil: true
validates :event_id, uniqueness: { scope: :app_setting_option_id}, allow_nil: true
validates :app_level_setting, uniqueness: { scope: :app_setting_option_id}, allow_nil: true
On top of all that, I also have in place a compound unique index on each of the secondary objects in the database. Normally I would just rely on ActiveRecord validations to take care of any weird edge case scenarios, but (long story short) there tends to be a decent amount of direct database adjustments that happen and we want to stop something unintended from happening.
In my tests, I create an app_setting with a specific app_setting_option and a specific user_id and then try to do the exact same thing again. Validations stops that from happening. It works flawlessly with user_role, event, and group as well. The problem is with the app_level_setting where it somehow makes it past validations and gets stopped by the compound unique index at the database level, getting a Mysql2::Error: Duplicate entry.
it 'app_level_setting uniqueness' do
app_setting_option = AppSettingOption.find(21)
create(:app_setting, app_setting_option: app_setting_option, app_level_setting: 1)
create(:app_setting, app_setting_option: app_setting_option, app_level_setting: 1)
end
The 2nd app_setting is not created because of the index on the database, not because of the validations. Is there something specific about uniqueness validations on a boolean? I get the same error when I try to create an identical app_level_setting in the console as well.
回答1:
For boolean field validations you can not set uniquesness validation. For validating boolean fields, you can do something like,
validates :field, :inclusion => {:in => [true, false]}
回答2:
In the end, I had to create a custom validation as follows:
validate :app_level_setting_validation
def app_level_setting_validation
errors.add(:app_setting, "app level setting already exists") if app_setting_option.app_setting_type_id == 1 && AppSetting.find_by(app_setting_option_id: app_setting_option.id, app_level_setting: true).present?
end
And then my test was as follows:
it 'App level setting uniqueness' do
app_setting_option = create(:app_setting_option)
create(:app_setting, app_setting_option: app_setting_option, app_level_setting: true)
setting = build(:app_setting, app_setting_option: app_setting_option, app_level_setting: true)
setting.should_not be_valid
end
来源:https://stackoverflow.com/questions/30384297/activerecord-scoped-uniqueness-validations-vs-mysql-compound-unique-indexes