2012-02-20 12 views
19

estoy Mapeo 2 modelos:modelo de rieles que tiene tanto 'has_one' y 'has_many' pero con algunas contraints

User 
Account 

class Account 
    has_many :users 


class User 
    has_one :account 

La tabla de usuario como el account_id en ella.

Ahora, en el modelo de Cuenta, quiero crear un "usuario principal" cuya cuenta solo tiene 1 desactivado. La tabla de usuario tiene un indicador booleano: is_primary, ¿cómo puedo crear un has_one en el lado de la cuenta para un usuario que tiene el is_primary y el account_id asignados?

Así que el SQL se vería así:

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

Así que quiero:

Un usuario tiene una cuenta. Una cuenta tiene muchos usuarios y también tiene un único usuario principal.

Respuesta

41

Enfoque 1 - Añadir una nueva asociación

Añadir una asociación has_one con una lambda where. Esto le permite trabajar dentro de su esquema actual.

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

Ahora:

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]') 

Enfoque 2 - Añadir un método de asociación

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

Ahora:

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

¡gracias por proporcionar opciones! – Blankman

+0

agradecería sus comentarios sobre esto, ya que está relacionado: http://stackoverflow.com/questions/9365068/rails-model-that-has-both-has-one-and-has-many-but-with-some -contraints – Blankman

+0

enfoque agradable y limpio. ¿Le importaría explicar cómo actualizar el primary_user en un formulario de actualización de cuenta # con una selección (collection_) de todos los usuarios, utilizando el método 1? Gracias – Patient55

6

Probablemente sería más fácil de agregar un campo primary_user_id a cuenta y agregar una asociación de 'has_one' para la primary_user:

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

class User 
    has_one :account 
end 

Si tiene que utilizar el esquema existente (con la bandera booleana is_primary) , se puede agregar un ámbito como éste:

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

y luego encadenar el alcance de las operaciones de búsqueda de los usuarios:

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

+1 para el primer enfoque. Aunque 'account.users.primary' es más expresivo que' account.users.primary.first' –

+1

Muy cierto, ya que la convención de nomenclatura no fluye bien. Pero esto se rectifica fácilmente llamando al ámbito 'primarios', y haciendo que un método de clase se defina como' def self.primary; primarios. primero; fin'. Ahora puede usar el alcance general 'primaries', o el método' primary' para referirse a un solo registro (donde solo se espera 1) – PinnyM

Cuestiones relacionadas