Rails has_many: via la recherche par attributs supplémentaires dans le modèle de jointure
-
03-07-2019 - |
Question
Nouveau pour Ruby et Rails, mais je suis un livre déjà éduqué (ce qui ne veut apparemment rien dire, haha).
J'ai deux modèles, Event et User joints via une table 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
Ce projet est un calendrier dans lequel je dois garder une trace des personnes qui s'inscrivent et qui grattent leur nom pour un événement donné. Je pense que plusieurs à plusieurs est une bonne approche, mais je ne peux pas faire quelque chose comme ça:
u = User.find :first
active_events = u.events.find_by_active(true)
Etant donné que les événements ne possèdent PAS ces données supplémentaires, c'est le modèle EventUser. Et pendant que je pouvais faire:
u = User.find :first
active_events = []
u.event_users.find_by_active(true).do |eu|
active_events << eu.event
end
Cela semble être contraire à "the rails way". Quelqu'un peut-il m'éclairer, cela me dérange depuis longtemps ce soir (ce matin)?
La solution
Pourquoi ne pas ajouter quelque chose comme cela dans votre modèle d'utilisateur?
has_many :active_events, :through => :event_users,
:class_name => "Event",
:source => :event,
:conditions => ['event_users.active = ?',true]
Ensuite, vous devriez pouvoir obtenir des événements actifs pour un utilisateur en appelant simplement:
User.first.active_events
Autres conseils
Milan Novota a une bonne solution - mais : conditions
est maintenant obsolète et le : conditions = > Le bit ['event_users.active =?', true]
ne semble tout simplement pas très gai. Je préfère quelque chose comme ça:
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
Après cela, vous devriez toujours pouvoir obtenir des événements actifs pour un utilisateur simplement en appelant:
User.first.active_events
Même si votre u.events n'appelle pas explicitement la table user_events, cette table est toujours incluse dans le SQL implicitement en raison des jointures nécessaires. Vous pouvez donc toujours utiliser cette table dans vos conditions de recherche:
u.events.find(:all, :conditions => ["user_events.active = ?", true])
Bien sûr, si vous envisagez de faire souvent cette recherche, assurez-vous de lui associer une association distincte, comme le suggère Milan Novota. >
Dans le modèle Utilisateur
, le domaine de responsabilité est plus important que nécessaire, et il n'y a aucune bonne raison de le faire.
Nous pouvons d’abord définir l’étendue dans le modèle EventUser
car son emplacement réel, comme:
class EventUser < ActiveRecord::Base
belongs_to :event
belongs_to :user
scope :active, -> { where(active: true) }
scope :inactive, -> { where(active: false) }
end
À présent, un utilisateur peut avoir les deux types d'événements: les événements actifs et les événements inactifs, afin que nous puissions définir la relation dans le modèle Utilisateur
comme suit:
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 beauté de cette technique réside dans le fait que la fonctionnalité d’événement actif ou inactif appartient au modèle EventUser
, et si à l’avenir la fonctionnalité doit être modifiée, elle ne sera modifiée que dans un seul cas. place: EventUser
, et les modifications seront répercutées dans tous les autres modèles.