Rails has_many: a través de Buscar por atributos adicionales en Unir modelo
-
03-07-2019 - |
Pregunta
Nuevo para Ruby y Rails, pero ahora estoy educado en libros (lo que aparentemente no significa nada, jaja).
Tengo dos modelos, Evento y Usuario 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 tachan su nombre para un evento determinado. Pienso 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 esos datos adicionales, el modelo EventUser sí lo tiene. Y mientras pude hacerlo:
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 " la manera de los carriles " ;. ¿Alguien me puede iluminar, esto me ha estado molestando durante mucho tiempo esta noche (esta mañana)?
Solución
¿Qué te parece agregar algo como esto a tu 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ías poder obtener eventos activos para un usuario simplemente llamando:
User.first.active_events
Otros consejos
Milan Novota tiene una buena solución, pero : condition
ahora está en desuso y el : condition = > ['event_users.active =?', true]
simplemente no parece ser una barandilla. 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 simplemente llamando:
User.first.active_events
A pesar de que u.events no está explícitamente llamando a la tabla user_events, esa tabla aún se incluye en el SQL implícitamente debido a las combinaciones necesarias. Por lo tanto, aún puedes usar esa tabla en tus condiciones de búsqueda:
u.events.find(:all, :conditions => ["user_events.active = ?", true])
Por supuesto, si planeas hacer esta búsqueda mucho, entonces seguro, dale una asociación separada como sugiere Milan Novota, pero no hay requisito para que lo hagas de esa manera
Bueno, se pone más responsabilidad en el modelo Usuario
de lo que realmente se necesita, y no hay una buena razón para hacerlo.
Primero podemos definir el alcance en el modelo EventUser
porque donde realmente 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 y eventos inactivos, por lo que podemos definir la relación en el modelo Usuario
de la 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ía solo en uno Coloque: EventUser
, y los cambios se reflejarán en todos los demás modelos.