2015-12-29 10 views
6

A link ma dwa components: componenta_id i componentb_id. W tym celu w pliku Link modelu mam:Sposób modelowania tego złożonego sprawdzania poprawności unikalności na połączonych polach

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 } 

A w pliku migracji:

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 

Pytanie: To wszystko działa. Teraz chcę, aby kombinacja componanta i componentb była unikalna, niezależnie od ich kolejności. Niezależnie od tego, który komponent to componenta, a który z nich to componentb (po tym wszystkim jest to samo łącze, połączenie między dwoma tymi samymi komponentami). Tak więc dwa poniższe zapisy nie powinny być dozwolone, ponieważ reprezentują to samo łącze, a zatem nie są unikalne:

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

Jak mogę utworzyć tę walidację niepowtarzalności? Mam sprawdzania poprawności modelu (patrz poniżej), ale zastanawiam się, czy i jak powinienem również dodać sprawdzania poprawności na poziomie migracji/db ...?


walidacji modelu
Mam walidacji modelu pracy z poniższym kodzie:

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 

Poniższe badanie potwierdza powyższe prace:

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 

Migracja/db sprawdzania poprawności:
Jako dodatkowy poziom bezpieczeństwa, czy istnieje również sposób na włączenie sprawdzania poprawności dla tego na poziomie db? W przeciwnym razie nadal można zapisać oba następujące rekordy do bazy danych: componenta_id = 1 ; componentb_id = 2, a także componenta_id = 2 ; componentb_id = 1.

+0

W [rozmowa] (http://stackoverflow.com/questions/635937/how-do-i-specify-unique-constraint-for-multiple-columns-in-mysql) sugeruje się utworzenie relacji wiele do wielu: ' ma wiele elementów: " 'validates_length_of: components, maximum: 2' – skahlert

Odpowiedz

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

Dzięki! ale czy nie pozwoliłoby to na połączenie 1-2, a także 2-1. Czy oznacza to, że komponent o id 1 to 'componenta', a komponent o identyfikatorze 2 to' componentb', a raz na odwrót? Właśnie tego chcę zapobiec. Połączenie 1-2 powinno być unikalne, niezależnie od tego, który z nich to "componenta", a który to "componentb". – Nick

+0

Tak, właśnie dlatego zamierzałem usunąć odpowiedź; Myślałem, że zostawię to, dopóki nie zostanie odrzucone: D –

+0

Dlaczego nie spróbować powyższych? Nie jest super sprytny, ale może uzyskać oczekiwany rezultat. –

4

Być może możliwe jest kontrolowanie tworzenia powiązań z:

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 

Jeśli potrzebujesz walidacji, a następnie można zwyczaj sprawdzania poprawności:

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

Ale to jest tylko walidacja modelu, prawda? W celu sprawdzenia poprawności modelu kod taki jak ja to działa. Jest to walidacja na poziomie db, więc mam na myśli, że w pliku migracyjnym wciąż brakuje. – Nick

+0

Nie mogę wymyślić ogólnego sposobu na poziomie bazy danych. Być może użyć jakiejś formy wyzwalacza bazy danych, aby temu zapobiec? Mogą również istnieć metody specyficzne dla dostawcy baz danych. – roob

Powiązane problemy