66 votes

has_and_belongs_to_many, en évitant les dupes dans la table de jointure

J'ai un ensemble de modèles HABTM assez simple

 class Tag < ActiveRecord::Base 
   has_and_belongs_to_many :posts
end 

class Post < ActiveRecord::Base 
   has_and_belongs_to_many :tags

   def tags= (tag_list) 
      self.tags.clear 
      tag_list.strip.split(' ').each do 
        self.tags.build(:name => tag) 
      end
   end 
end
 

Maintenant, tout fonctionne bien, sauf que je reçois une tonne de doublons dans le tableau Tags.

Que dois-je faire pour éviter les doublons (bases sur le nom) dans la table des balises?

25voto

spyle Points 413

En plus des suggestions ci-dessus:

  1. ajouter :uniq à l'association has_and_belongs_to_many
  2. ajout d'index unique sur la table de jointure

Je voudrais faire une vérification explicite pour déterminer si la relation existe déjà. Par exemple:

 post = Post.find(1)
tag = Tag.find(2)
post.tags << tag unless post.tags.include?(tag)
 

20voto

Simone Carletti Points 77653

Vous pouvez passer l' :uniq option comme décrit dans la documentation. Notez également que l' :uniq options n'est pas d'éviter la création de doublons de relations, il assure accesseur/trouver des méthodes de sélectionner une seule fois.

Si vous voulez éviter les doublons dans la table d'association, vous devez créer un index unique et gérer l'exception. Aussi validates_uniqueness_of ne fonctionne pas comme prévu, car vous pouvez tomber dans le cas d'une deuxième demande est écrit à la base de données entre le moment de la première demande, vérifie les doublons et les écrit dans la base de données.

20voto

nerith Points 826

Dans Rails4:

 class Post < ActiveRecord::Base 
  has_and_belongs_to_many :tags, -> { uniq }
 

(attention, les -> { uniq } doivent se situer juste après le nom de la relation, avant les autres paramètres)

Documentation Rails

12voto

Joshua Cheek Points 9450

Définissez l'option uniq:

 class Tag < ActiveRecord::Base 
   has_and_belongs_to_many :posts , :uniq => true
end 

class Post < ActiveRecord::Base 
   has_and_belongs_to_many :tags , :uniq => true
 

4voto

Sam Saffron Points 56236

J'ai résolu ce problème en créant un filtre before_save qui corrige les problèmes.

 class Post < ActiveRecord::Base 
   has_and_belongs_to_many :tags
   before_save :fix_tags

   def tag_list= (tag_list) 
      self.tags.clear 
      tag_list.strip.split(' ').each do 
        self.tags.build(:name => tag) 
      end
   end  

    def fix_tags
      if self.tags.loaded?
        new_tags = [] 
        self.tags.each do |tag|
          if existing = Tag.find_by_name(tag.name) 
            new_tags << existing
          else 
            new_tags << tag
          end   
        end

        self.tags = new_tags 
      end
    end

end
 

Il pourrait être légèrement optimisé pour travailler par lots avec les balises, mais il pourrait également nécessiter un support transactionnel légèrement supérieur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X