ruby-on-rails - inverse_of - rails references
Rails has_many: a través de Buscar por atributos adicionales en Join Model (4)
Nuevo en Ruby and Rails, pero ahora tengo un libro educado (que aparentemente no significa nada, jaja).
Tengo dos modelos, Event y User unidos a través de una tabla EventUser
class User < ActiveRecord::Base
has_many :event_users
has_many :events, :through => :event_users
end
class EventUser < ActiveRecord::Base
belongs_to :event
belongs_to :user
#For clarity''s sake, EventUser also has a boolean column "active", among others
end
class Event < ActiveRecord::Base
has_many :event_users
has_many :users, :through => :event_users
end
Este proyecto es un calendario, en el que tengo que hacer un seguimiento de las personas que se registran y rascan su nombre para un evento determinado. Me imagino que muchos a muchos es un buen enfoque, pero no puedo hacer algo como esto:
u = User.find :first
active_events = u.events.find_by_active(true)
Debido a que los eventos en realidad NO TIENEN esa información adicional, el modelo EventUser sí lo hace. Y mientras yo podría hacer:
u = User.find :first
active_events = []
u.event_users.find_by_active(true).do |eu|
active_events << eu.event
end
Esto parece ser contrario a "el camino de los rieles". ¿Alguien puede iluminarme, esto me ha estado molestando por mucho tiempo esta noche (esta mañana)?
¿Qué le parece agregar algo como esto en su modelo de Usuario?
has_many :active_events, :through => :event_users,
:class_name => "Event",
:source => :event,
:conditions => [''event_users.active = ?'',true]
Después de eso, debería poder obtener eventos activos para un usuario con solo llamar:
User.first.active_events
Aunque u.events no está llamando explícitamente a la tabla user_events, esa tabla todavía se incluye en el SQL implícitamente debido a las combinaciones necesarias. Por lo tanto, aún puede usar esa tabla en sus condiciones de búsqueda:
u.events.find(:all, :conditions => ["user_events.active = ?", true])
Por supuesto, si planeas hacer esta búsqueda mucho, seguro, dale una asociación por separado, como sugiere Milan Novota, pero no es necesario que lo hagas de esa manera.
Bueno, se está poniendo más responsabilidad en el modelo de User
de lo realmente necesario, y no hay una buena razón para hacerlo.
Primero podemos definir el alcance en el modelo de EventUser
de EventUser
porque en realidad pertenece, como:
class EventUser < ActiveRecord::Base
belongs_to :event
belongs_to :user
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
end
Ahora, un usuario podría tener ambos tipos de eventos: eventos activos, así como eventos inactivos, por lo que podemos definir la relación en el modelo de User
siguiente manera:
class User < ActiveRecord::Base
has_many :active_event_users, -> { active }, class_name: "EventUser"
has_many :inactive_event_users, -> { inactive }, class_name: "EventUser"
has_many :inactive_events, through: :inactive_event_user,
class_name: "Event",
source: :event
has_many :active_events, through: :active_event_users,
class_name: "Event",
source: :event
end
La belleza de esta técnica es que la funcionalidad de ser un evento activo o inactivo pertenece al modelo EventUser
, y si en el futuro la funcionalidad necesita ser modificada, se modificará solo en un lugar: el modelo de EventUser
, y los cambios serán reflejado en todos los otros modelos.
Milan Novota tiene una buena solución, pero :conditions
ahora están en desuso y el bit de :conditions => [''event_users.active = ?'',true]
no parece muy bueno. Prefiero algo como esto:
has_many :event_users
has_many :active_event_users, -> { where active: true }, class_name: ''EventUser''
has_many :active_events, :through => :active_event_users, class_name: ''Event'', :source => :event
Después de eso, aún debería poder obtener eventos activos para un usuario con solo llamar:
User.first.active_events