2012-02-20 16 views
19

sto mappatura 2 modelli:Rails modello che ha sia 'has_one' e 'has_many' ma con alcuni vincoli

User 
Account 

class Account 
    has_many :users 


class User 
    has_one :account 

La tabella utente come account_id in esso.

Ora sul modello Conto voglio creare un 'utente principale' che un account ha solo 1 spento. La tabella utente ha un flag booleano: is_primary, come posso creare un has_one sul lato account per un utente che ha mappato is_primary e account_id.

Così lo SQL sarà simile:

SELECT * FROM users where account_id=123 and is_primary = 1 

quindi voglio:

Un utente ha un account. Un account ha molti utenti e ha anche un singolo utente principale.

risposta

41

Approccio 1 - Aggiungere una nuova associazione

Aggiungere un has_one associazione con una where lambda. Questo ti permette di lavorare all'interno del tuo schema attuale.

class Account 
    has_many :users 
    has_one :primary_user, -> { where(is_primary: true) }, :class_name=> "User" 
end 

Ora:

account.users #returns all users associated with the account 
account.primary_user #returns the primary user associated with the account 
# creates a user with is_primary set to true 
account.build_primary_user(name: 'foo bar', email: '[email protected]') 

Approccio 2 - Aggiungi un metodo di associazione

class Account 
    has_many :users do 
    def primary 
     where(:is_primary => true).first 
    end 
    end 
end 

Ora:

account.users.primary # returns the primary account 
+0

grazie per aver fornito le opzioni! – Blankman

+0

apprezzerebbero i tuoi commenti su questo, poiché è correlato: http://stackoverflow.com/questions/9365068/rails-model-that-has-both-has-one-and-has-many-but-with-some -contraints – Blankman

+0

Approccio piacevole e pulito. Ti dispiacerebbe spiegare come aggiornare l'utente primario in un modulo # di aggiornamento dell'account con una selezione (raccolta_) di tutti gli utenti, utilizzando l'approccio 1? Grazie – Patient55

6

Probabilmente sarebbe più semplice per aggiungere un campo primary_user_id per account e aggiungere un associazione 'has_one' per la primary_user:

class Account 
    has_many :users 
    has_one :primary_user, :class_name => "User" 
end 

class User 
    has_one :account 
end 

Se è necessario utilizzare lo schema esistente (con il: flag booleano is_primary) , è possibile aggiungere un ambito come questo:

class User 
    has_one :account 
    scope :primary, where(:is_primary => true) 
end 

e poi concatenare il campo di applicazione alla ricerca degli utenti:

account = Account.find(1) 
primary_user = account.users.primary.first 
+0

+1 per il primo approccio. Sebbene 'account.users.primary' sia più espressivo di' account.users.primary.first' –

+1

Molto vero in quanto la convenzione di denominazione non scorre bene. Ma questo è facilmente rettificato chiamando scope 'primaries', e facendo un metodo di classe definito come' def self.primary; primaries.first; FINE'. Ora puoi usare i 'primari scope 'generali, o il metodo' primary' per fare riferimento a un singolo record (dove solo 1 è previsto) – PinnyM

Problemi correlati