web-dev-qa-db-fra.com

Quel est l'équivalent de l'option has_many 'conditions' dans Rails 4?

Quelqu'un peut-il me dire quelle est la manière équivalente de faire la ligne suivante dans Rails 4?

has_many :friends, :through => :friendships, :conditions => "status = 'accepted'", :order => :first_name

J'ai essayé ce qui suit:

has_many :friends, -> { where status: 'accepted' }, :through => :friendships , :order => :first_name

Mais j'obtiens l'erreur suivante:

Invalid mix of scope block and deprecated Finder options on ActiveRecord association: User.has_many :friends
68
medBouzid

Doit être le deuxième argument:

class Customer < ActiveRecord::Base
  has_many :orders, -> { where processed: true }
end

http://edgeguides.rubyonrails.org/association_basics.html#scopes-for-has-any

RÉPONSE À LA MISE À JOUR:

Placez la commande à l'intérieur du bloc:

has_many :friends, -> { where(friendship: {status: 'accepted'}).order('first_name DESC') }, :through => :friendships
111
Kaleidoscope

Bien que d’autres réponses soient techniquement correctes, elles violent l’encapsulation. Le modèle User ne doit pas savoir que le modèle Amitié a une colonne appelée status et qu'il peut avoir une valeur spécifique telle que accepted.

Si vous décidez d’apporter une modification, pour tirer parti d’Enums dans Rails 4, par exemple, vous devrez modifier à la fois User et Amitié modèles. Cela pourrait conduire à des bogues évités par le maintien de l'encapsulation.

J'exposerais une portée dans le modèle Amitié:

scope :accepted, -> { where(status: :accepted) }

J'utiliserais ensuite cette portée dans le modèle User, en masquant tous les détails de la mise en œuvre à User.

has_many :friendships, -> { Friendship.accepted }
has_many :friends, through: :friendships

# Or...

has_many :friends, -> { Friendship.accepted }, through: :friendships

Vous pouvez aller plus loin et renommer la portée en accepted_friendships être plus clair.

has_many :accepted_friendships, -> { Friendship.accepted }
has_many :friends, through: :accepted_friendships

Vous avez maintenant encapsulé avec succès les détails de la mise en œuvre dans leurs modèles respectifs. Si quelque chose change, vous n’avez qu’un seul endroit pour le changer, ce qui réduit la maintenance et augmente la robustesse.

53
Mohamad

Une version de Rails 3.2 de la réponse de Mohamad serait la suivante):

class Friend < ActiveRecord::Base
  has_many :friendships, :order => :first_name

  has_many :friends, :through => :friendships,
           :conditions => proc { Friendship.accepted.where_ast }

  has_many :pending_friends, :through => :friendships,
           class_name => Friend,
           :conditions => proc { Friendship.pending.where_ast }
end

class Friendship < ActiveRecord::Base
  scope :status, ->(status) { where(:status => status) }
  scope :accepted, -> { status('accepted') }
  scope :pending, -> { where(arel_table[:status].not_eq('accepted')) } 
end

NOTES:

  • where_ast est important car il renvoie les nœuds AREL nécessaires au fonctionnement de la condition.
  • dans le proc passé à :conditions, self n'est pas toujours une instance de modèle (par exemple, lorsque l'association est fusionnée avec une autre requête)
  • L'utilisation de SQL bruts au sein de vos portées et de vos associations causera probablement des problèmes, à un moment donné, avec l'espacement des noms de table ... utilisez AREL.
4
br3nt

Pour travailler sur Rails 4.1 (mon cas), je devais mettre:

has_many :friends, -> { where(friendships: { status: 'accepted' }) }, through: :friendships

Notez le S sur les amitiés. Il fait directement référence au nom de la base de données.

2
Oswaldo Ferreira
has_many :friends, -> { where(status: 'accepted').order('frist_name')}, through: :friendships

ou

has_many :friends, -> { where(status: 'accepted').order(:frist_name)}, through: :friendships
0
Amrit Dhungana