ActiveRecord Scoped Uniqueness Validations vs MySQL Compound Unique Indexes

余生长醉 提交于 2019-12-10 11:16:55

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!