2015-12-29 2 views
6

A link имеет два components: componenta_id и componentb_id. Для этого в файле Link модели у меня есть:Как смоделировать эту комплексную проверку для уникальности на комбинированных полях

belongs_to :componenta, class_name: "Component" 
belongs_to :componentb, class_name: "Component" 

validates :componenta_id, presence: true 
validates :componentb_id, presence: true 
validates :componenta_id, uniqueness: { scope: :componentb_id } 
validates :componentb_id, uniqueness: { scope: :componenta_id } 

И в файле миграции:

create_table :links do |t| 
    t.integer :componenta_id, null: false 
    t.integer :componentb_id, null: false 
    ... 
end 
add_index :links, :componenta_id 
add_index :links, :componentb_id 
add_index :links, [:componenta_id, :componentb_id], unique: true 

Вопрос: Это все работает. Теперь я хочу, чтобы комбинация componanta и componentb была уникальной, независимо от их заказа. Таким образом, независимо от того, какой компонент равен componenta, а какой - componentb (в конце концов, это то же самое, связь между двумя одинаковыми компонентами). Таким образом, две приведенные ниже записи не должны допускаться, поскольку они представляют одну и ту же связь и поэтому не являются уникальными:

  • componenta_id = 1; componentb_id = 2
  • componenta_id = 2; componentb_id = 1

Как я могу создать эту проверку уникальности? Я работаю над проверкой модели (см. Ниже), но задаю вопрос, следует ли и как добавить подтверждение на уровне миграции/db ...?


проверки модели
Я валидация модели работы с кодом ниже:

before_save :order_links 
validates :componenta_id, uniqueness: { scope: :componentb_id } 

private 
    def order_links 
    if componenta_id > componentb_id 
     compb = componentb_id 
     compa = componenta_id 
     self.componenta_id = compb 
     self.componentb_id = compa 
    end 
    end 

Следующий тест подтверждает вышеуказанные работы:

1. test "combination of two links should be unique" do 
    2. assert @link1.valid? 
    3. assert @link2.valid? 
    4. @link1.componenta_id = 3  #@link2 already has combination 3-4 
    5. @link1.componentb_id = 4 
    6. assert_not @link1.valid? 
    7. @link1.componenta_id = 4 
    8. @link1.componentb_id = 3 
    9. assert_raises ActiveRecord::RecordNotUnique do 
    10. @link1.save 
    11. end 
    12.end 

Migration/db validation:
Как дополнительный уровень безопасности, есть ли способ включить проверку для этого на уровне db? В противном случае все еще можно записать в базу данных две следующие записи: componenta_id = 1 ; componentb_id = 2, а также componenta_id = 2 ; componentb_id = 1.

+0

В [этот разговор] (http://stackoverflow.com/questions/635937/how-do-i-specify-unique-constraint-for-multiple-columns-in-mysql) предложение состоит в том, чтобы создать отношение «многие ко многим»: ' имеет много компонентов: 'validates_length_of: компоненты, максимум: 2' – skahlert

ответ

2
validates :componenta_id, uniqueness: { scope: :componentb_id } 
validates :componentb_id, uniqueness: { scope: :componenta_id } 
+0

Спасибо! но это все равно не позволит использовать комбинацию 1-2, а также 2-1. Значение один раз, когда компонент с id 1 является 'componenta', а компонент с id 2 является' componentb', а один раз наоборот? Это то, что я хочу предотвратить. Комбинация 1-2 должна быть уникальной, независимо от того, какая из двух является 'componenta' и которая является' componentb'. – Nick

+0

Да, именно поэтому я собирался удалить ответ; Я думал, что оставлю его, если он не проголосовал: D –

+0

Почему бы не попробовать выше? Не супер умный, но может получить нужный результат –

4

Возможно, можно управлять созданием связей с:

def create_unique_link(comp_1, comp_2) 
    if comp_1.id > comp_2.id 
    first_component = comp_1 
    second_component = comp_2 
    end 
    link = Link.find_or_create_by(componenta_id: first_comp.id, componentb_id: second_comp.id) 
end 

Если вам нужно подтверждение, то вы можете пользовательские проверки:

def ensure_uniqueness_of_link 
    if comp_1.id > comp_2.id 
    first_component = comp_1 
    second_component = comp_2 
    end 

    if Link.where(componenta_id: first_component.id, componentb_id: second_component).first 
    errors.add(:link, 'Links should be unique') 
    end 

end 
+0

Но это все проверки модели, не так ли? Для проверки модели код, как и у меня, работает. Это проверка на уровне db, поэтому я имею в виду в файле миграции, который все еще отсутствует. – Nick

+0

Я не могу представить общий способ на уровне базы данных. Возможно, используется некоторая форма триггера базы данных, чтобы предотвратить его? Также могут существовать специфические методы поставщика базы данных. – roob

Смежные вопросы