Question

Comment puis-je atteindre les objectifs suivants? J'ai deux modèles (blogs et lecteurs) et une table JOIN qui me permettront d'avoir une relation N: M entre eux:

class Blog < ActiveRecord::Base
  has_many :blogs_readers, :dependent => :destroy
  has_many :readers, :through => :blogs_readers
end

class Reader < ActiveRecord::Base
  has_many :blogs_readers, :dependent => :destroy
  has_many :blogs, :through => :blogs_readers
end

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader
end

Ce que je veux faire maintenant, c'est ajouter des lecteurs à différents blogs. La condition, cependant, est que je ne peux ajouter qu'un lecteur à un blog UNE FOIS. Il ne doit donc pas y avoir de doublons (même readerID, même blogID) dans la table BlogsReaders. Comment puis-je y parvenir?

La deuxième question est la suivante: comment obtenir une liste de blogs à laquelle les lecteurs ne sont pas encore abonnés (par exemple, pour remplir une liste de sélection déroulante, qui peut ensuite être utilisée pour ajouter le lecteur à un autre blog)?

Était-ce utile?

La solution

Qu'en est-il de:

Blog.find(:all,
          :conditions => ['id NOT IN (?)', the_reader.blog_ids])

Rails s’occupe de la collecte des identifiants pour nous avec des méthodes d’association! :)

http://api.rubyonrails.org/classes/ActiveRecord/Associations/ ClassMethods.html

Autres conseils

Une solution plus simple intégrée à Rails:

 class Blog < ActiveRecord::Base
     has_many :blogs_readers, :dependent => :destroy
     has_many :readers, :through => :blogs_readers, :uniq => true
    end

    class Reader < ActiveRecord::Base
     has_many :blogs_readers, :dependent => :destroy
     has_many :blogs, :through => :blogs_readers, :uniq => true
    end

    class BlogsReaders < ActiveRecord::Base
      belongs_to :blog
      belongs_to :reader
    end

Notez l'ajout de l'option :uniq => true à l'appel has_many.

Vous pouvez également prendre en compte has_and_belongs_to_many entre Blog et Reader, à moins que vous n'ayez d'autres attributs que vous souhaiteriez avoir dans le modèle de jointure (ce que vous n'avez pas actuellement). Cette méthode a également un :uniq opiton.

Notez que cela ne vous empêche pas de créer les entrées dans la table, mais veille à ce que lorsque vous interrogez la collection, vous n'obtenez qu'un seul de chaque objet.

Mettre à jour

Dans Rails 4, le moyen de le faire consiste à utiliser un bloc scope. Le texte ci-dessus devient.

class Blog < ActiveRecord::Base
 has_many :blogs_readers, dependent:  :destroy
 has_many :readers,  -> { uniq }, through: :blogs_readers
end

class Reader < ActiveRecord::Base
 has_many :blogs_readers, dependent: :destroy
 has_many :blogs, -> { uniq }, through: :blogs_readers
end

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader
end

Mise à jour pour Rails 5

L'utilisation de uniq dans le bloc de la portée provoquera une erreur NoMethodError: undefined method 'extensions' for []:Array. Utilisez distinct à la place:

class Blog < ActiveRecord::Base
 has_many :blogs_readers, dependent:  :destroy
 has_many :readers,  -> { distinct }, through: :blogs_readers
end

class Reader < ActiveRecord::Base
 has_many :blogs_readers, dependent: :destroy
 has_many :blogs, -> { distinct }, through: :blogs_readers
end

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader
end

Ceci devrait répondre à votre première question:

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader

  validates_uniqueness_of :reader_id, :scope => :blog_id
end

La méthode Rails 5.1

class Blog < ActiveRecord::Base
 has_many :blogs_readers, dependent:  :destroy
 has_many :readers,  -> { distinct }, through: :blogs_readers
end

class Reader < ActiveRecord::Base
 has_many :blogs_readers, dependent: :destroy
 has_many :blogs, -> { distinct }, through: :blogs_readers
end

class BlogsReaders < ActiveRecord::Base
  belongs_to :blog
  belongs_to :reader
end

La réponse sur ce lien montre comment remplacer le " < < " méthode pour obtenir ce que vous recherchez sans lever d’exception ni créer de méthode distincte: idiome Rails pour éviter les doublons dans has_many: via

Je pense que quelqu'un viendra avec une meilleure réponse que celle-là.

the_reader = Reader.find(:first, :include => :blogs)

Blog.find(:all, 
          :conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])

[edit]

Veuillez consulter la réponse de Josh ci-dessous. C'est le chemin à parcourir. (Je savais qu'il y avait un meilleur moyen de sortir;)

La réponse principale indique actuellement l'utilisation de uniq dans le proc:

class Blog < ActiveRecord::Base
 has_many :blogs_readers, dependent:  :destroy
 has_many :readers,  -> { uniq }, through: :blogs_readers
end

Cependant, cela entame la relation dans un tableau et peut casser les choses qui sont censées effectuer des opérations sur une relation, pas un tableau.

Si vous utilisez distinct le conserve comme relation:

class Blog < ActiveRecord::Base
 has_many :blogs_readers, dependent:  :destroy
 has_many :readers,  -> { distinct }, through: :blogs_readers
end

Le moyen le plus simple consiste à sérialiser la relation dans un tableau:

class Blog < ActiveRecord::Base
  has_many :blogs_readers, :dependent => :destroy
  has_many :readers, :through => :blogs_readers
  serialize :reader_ids, Array
end

Ensuite, lorsque vous attribuez des valeurs aux lecteurs, vous les appliquez comme suit:

blog.reader_ids = [1,2,3,4]

Lorsque vous affectez des relations de cette manière, les doublons sont automatiquement supprimés.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top