2015-12-29 14 views
6

A link ha due components: componenta_id e componentb_id. A tal fine, nel file Link modello che ho:Come modellare questa convalida complessa per l'univocità nei campi combinati

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 } 

E nel file di migrazione:

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 

Domanda: Questa tutte le opere. Ora voglio che la combinazione di componanta e componentb sia unica, indipendentemente dal loro ordine. Quindi, indipendentemente da quale componente sia componenta e quale sia componentb (dopotutto è lo stesso link, un collegamento tra i due stessi componenti). Così i due record di seguito non dovrebbe essere consentito in quanto rappresentano lo stesso link e quindi non sono unici:

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

Come posso creare questa convalida di unicità? Ho una validazione del modello funzionante (vedi sotto) ma mi chiedo se e come dovrei aggiungere anche la validazione a livello di migrazione/db ...?


validazione del modello
ho convalida modello di lavoro con il codice qui sotto:

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 

Il seguente test conferma le opere di cui sopra:

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 

Migrazione/convalida db:
Come ulteriore livello di sicurezza, esiste anche un modo per incorporare la convalida a livello di db? In caso contrario, è ancora possibile scrivere entrambi i seguenti record nel database: componenta_id = 1 ; componentb_id = 2 e componenta_id = 2 ; componentb_id = 1.

+0

In [questa conversazione] (http://stackoverflow.com/questions/635937/how-do-i-specify-unique-constraint-for-multiple-columns-in-mysql) il suggerimento è di creare una relazione molti-a-molti: ' ha molti: componenti ' 'validates_length_of: components, maximum: 2' – skahlert

risposta

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

Grazie! ma questo non permetterebbe ancora una combinazione di 1-2 e 2-1. Significa una volta che il componente con id 1 è 'componententa' e il componente con id 2 è' componentb', e una volta il contrario? Questo è quello che voglio impedire. La combinazione 1-2 dovrebbe essere unica, indipendentemente da quale dei due sia 'componenta' e quale' componentb'. – Nick

+0

Sì, è per questo che stavo per cancellare la risposta; Ho pensato di lasciarlo a meno che non fosse stato ritirato: D –

+0

Perché non provi quanto sopra? Non molto intelligente, ma potrebbe ottenere il risultato che ti serve –

4

forse è possibile controllare la creazione dei collegamenti con:

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 

Se avete bisogno della convalida, allora è possibile convalidare personalizzato:

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

Ma questa è la convalida del modello, giusto? Per la validazione del modello, il codice funziona correttamente. È una convalida a livello di database, quindi, nel file di migrazione, manca ancora. – Nick

+0

Non riesco a pensare a un modo generico a livello di database. Forse utilizzare qualche forma di trigger del database per impedirlo? Potrebbero esistere anche metodi specifici del fornitore di database. – roob

Problemi correlati