2011-08-09 2 views
14

Ho un modello con una colonna active, che è un valore booleano. Voglio convalidare l'unicità di tutti i record appena aggiunti rispetto allo company_id, in modo tale da poter aggiungere tutti i record che voglio con lo stesso company_id alla tabella finché active è impostato su false. Ci dovrebbe essere solo un record active per ogni company_id.Rails 3 convalida univocità di un valore rispetto a una colonna?

Come scriverei questo? Ho già provato:

validates :company_id, :uniqueness => { :scope => :active } 

ma che sembra convalidare anche contro combinazioni uniche di active essere false (in modo tale che non potrò mai avere più di due company_id s' nella tabella con la stessa active status, a prescindere dalla cosa è active) - la convalida di cui sopra consente due record per company_id, uno con active = false e l'altro con active = true. Una volta che quei due record sono in, la convalida blocca tutto il resto.

Allora ho provato ad aggiungere questo:

scope :active, where(:active => true) 

Ma questo non sembra aver cambiato la validazione a tutti (stesso problema come sopra).

Come posso scrivere questo convalida in modo che io possa aggiungere il numero di record con lo stesso company_id finché active è falso, e consentendo solo una active = true per company_id?

risposta

13

Non è necessario utilizzare validates_each - questo è solo se si desidera passare più attributi attraverso lo stesso blocco. Basta creare una convalida personalizzata:

validate :company_id_when_active 

def company_id_when_active 
    if active? and CompanyTerm.exists? ["company_id = ? AND active = 1 AND id != ?", company_id, id.to_i] 
    errors.add(:company_id, 'already has an active term') 
    end 
end 
-2
validates :company_id, :uniqueness => { :scope => :active } unless Proc.new { self.active } 

E forse si desidera chiamare società stessa

validates :company, :uniqueness => { :scope => :active } unless Proc.new { self.active } 

EDIT

seguente risposta in base alla tua soluzione è un po 'più efficiente:

scope :active, where(:active => true) 

validates_each :company do |model, attr, value| 
    model.errors.add(attr, 'already has an active term') if CompanyTerm.active.exists?(:company_id => value) 
end 
+0

Questo non rende il mio primo passaggio banco di prova, in cui cerco di aggiungere un nuovo record in una tabella vuota con 'active' impostato a' true', quindi convalidare con o non un altro nuovo record con 'active' impostato su' true' è valido o meno (entrambi con lo stesso 'company_id'). In realtà, sembra che il Proc non venga mai chiamato. – neezer

+0

Questo _answer_ è rotto in molti modi. – smathy

4

Ok, Penso di aver finalmente capito questo.

Checking Up On Rails Guide mi ha portato a validates_each, che mi ha portato a questa soluzione:

scope :active, where(:active => true) 

validates_each :company do |model, attr, value| 
    active = CompanyTerm.active.where(:company_id => value) 
    model.errors.add(attr, 'already has an active term') unless active.empty? 
end 

Non so se questo è il modo più efficace per scrivere questo, ma funziona. Sono aperto a tutti e tutti i suggerimenti!

1

Vecchio argomento, ma esiste la soluzione più semplice. La risposta sopra è quasi corretta. È possibile utilizzare :if economico come una chiave, come:

validates :active, :uniqueness => { :scope => :company_id }, :if => :active 
+0

ma è ancora possibile assegnare tutto a 'true' usando' update_all (: active => false) ' – jdscosta91

+3

Questo è sbagliato - controlla il valore di active sul modello di validazione, non su tutti gli altri record – koosa

+0

su @koosa: Potresti dimmi perché questo è sbagliato? se il modello di validazione è inattivo (attivo = falso), è OK non convalidare l'unicità di attivo nell'ambito della società –

6

Rails 4+ offre una soluzione molto migliore per questo.

validates_uniqueness_of :company_id, conditions: -> { where(active: true) } 

check out documentation