Rails has_many: tramite Find by Extra Attributes in Join Model
-
03-07-2019 - |
Domanda
Nuovo sia per Ruby che per Rails, ma ormai sono istruito sul libro (che apparentemente non significa niente, ahah).
Ho due modelli, Event e User uniti tramite un EventUser tabella
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
Questo progetto è un calendario, in cui devo tenere traccia delle persone che si iscrivono e grattano il loro nome per un determinato evento. Immagino che molti a molti sia un buon approccio, ma non posso fare qualcosa del genere:
u = User.find :first
active_events = u.events.find_by_active(true)
Poiché gli eventi in realtà NON HANNO quei dati extra, il modello EventUser lo fa. E mentre potrei fare:
u = User.find :first
active_events = []
u.event_users.find_by_active(true).do |eu|
active_events << eu.event
end
Questo sembra essere contrario al "modo di binari". Qualcuno può illuminarmi, questo mi ha infastidito per molto tempo stanotte (stamattina)?
Soluzione
Che ne dici di aggiungere qualcosa di simile nel tuo modello utente?
has_many :active_events, :through => :event_users,
:class_name => "Event",
:source => :event,
:conditions => ['event_users.active = ?',true]
Dopodiché dovresti essere in grado di ottenere eventi attivi per un utente semplicemente chiamando:
User.first.active_events
Altri suggerimenti
Milano Novota ha una buona soluzione & # 8211; ma : condizioni
è ora deprecato e : condizioni = > ['event_users.active =?', true]
bit non sembra comunque molto rotaia. Preferisco qualcosa del genere:
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
Dopodiché dovresti essere ancora in grado di ottenere eventi attivi per un utente semplicemente chiamando:
User.first.active_events
Anche se u.events non sta esplicitamente chiamando la tabella user_events, quella tabella è comunque inclusa in SQL implicitamente a causa dei join necessari. Quindi, puoi ancora usare quella tabella nelle tue condizioni di ricerca:
u.events.find(:all, :conditions => ["user_events.active = ?", true])
Ovviamente, se hai intenzione di fare molto questa ricerca, assicurati di dargli un'associazione separata come suggerisce Milan Novota, ma non c'è requisito per farlo in questo modo
Bene, nel modello User
è stata assegnata una maggiore responsabilità di quella effettivamente necessaria e non vi sono buoni motivi per farlo.
Possiamo innanzitutto definire l'ambito nel modello EventUser
perché a cui appartiene effettivamente, come:
class EventUser < ActiveRecord::Base
belongs_to :event
belongs_to :user
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
end
Ora, un utente potrebbe avere entrambi i tipi di eventi: eventi attivi ed eventi inattivi, quindi possiamo definire la relazione nel modello User
come segue:
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
Il bello di questa tecnica è che la funzionalità di essere un evento attivo o inattivo appartiene al modello EventUser
, e se in futuro la funzionalità dovesse essere modificata, sarebbe modificata solo in uno place: EventUser
e le modifiche si rifletteranno in tutti gli altri modelli.