durch Beziehung: Wie Duplikate in einem has_many zu vermeiden?
-
11-07-2019 - |
Frage
Wie kann ich folgendes erreichen? Ich habe zwei Modelle (Blogs und Leser) und eine Tabelle, JOIN, die mir erlauben, einen N zu haben: M Beziehung zwischen ihnen:
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
Was ich jetzt tun möchte, ist Leser in den verschiedenen Blogs. Die Bedingung ist allerdings, dass ich nur einen Leser zu einem Blog hinzufügen kann ONCE. Es muss also nicht alle Duplikate (gleicher readerID
, gleicher blogID
) in der BlogsReaders
Tabelle. Wie kann ich erreichen das?
Die zweite Frage ist, wie kann ich eine Liste von Blog zu bekommen, dass die Leser nicht abonniert ist bereits (zB eine Dropdown-Auswahlliste zu füllen, die dann verwendet werden kann, um den Leser zu einem anderen Blog)?
Lösung
Was ist mit:
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blog_ids])
Rails kümmert sich um die Sammlung von ids für uns mit Assoziationsmethoden! :)
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ ClassMethods.html
Andere Tipps
Einfachere Lösung das ist eingebaut in 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
Beachten Sie die :uniq => true
Option zum has_many
Gespräch hinzugefügt wird.
Auch möchten Sie vielleicht has_and_belongs_to_many
zwischen Blog und Reader betrachten, es sei denn, Sie einige andere Attribute würden Sie auf dem Join-Modell haben möchte (was man nicht tun, zur Zeit). Diese Methode hat auch eine :uniq
opiton.
Beachten Sie, dass dies verhindert nicht aus den Einträgen in der Tabelle erstellen, aber es ist sicher, dass, wenn Sie die Sammlung Abfrage erhalten Sie nur eine der einzelne Objekte.
Aktualisieren
In Rails 4 die Art und Weise zu tun, es über einen Umfang Block ist. Die oben genannten Änderungen.
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
Update für Rails 5
Die Verwendung von uniq
im Rahmen Block einen Fehler NoMethodError: undefined method 'extensions' for []:Array
verursachen. Verwenden distinct
statt:
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
Dies sollte Ihre erste Frage kümmern:
class BlogsReaders < ActiveRecord::Base
belongs_to :blog
belongs_to :reader
validates_uniqueness_of :reader_id, :scope => :blog_id
end
Die Rails 5.1 Art und Weise
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
Die Antwort auf diesen Link zeigt, wie das außer Kraft setzen „<<“ Methode zu erreichen, was Sie suchen, ohne Ausnahmen zu erhöhen oder eine separate Methode zu erstellen: Rails Idiom Duplikate in has_many zu vermeiden: durch
Ich denke, jemand mit einer besseren Antwort als diese kommen wird.
the_reader = Reader.find(:first, :include => :blogs)
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])
[Bearbeiten]
Bitte beachten Sie Joshs Antwort unten. Es ist der Weg zu gehen. (Ich wusste, dass es einen besseren Weg, war da draußen;)
Die Top-Antwort sagt derzeit uniq
im proc zu verwenden:
class Blog < ActiveRecord::Base
has_many :blogs_readers, dependent: :destroy
has_many :readers, -> { uniq }, through: :blogs_readers
end
Dies tritt jedoch die Beziehung in ein Array und kann die Dinge brechen, die Operationen auf eine Beziehung, kein Array.
auszuführen erwarten Wenn Sie distinct
es es als eine Beziehung hält:
class Blog < ActiveRecord::Base
has_many :blogs_readers, dependent: :destroy
has_many :readers, -> { distinct }, through: :blogs_readers
end
einfachste Weg ist, um die Beziehung zu einem Array serialisieren:
class Blog < ActiveRecord::Base
has_many :blogs_readers, :dependent => :destroy
has_many :readers, :through => :blogs_readers
serialize :reader_ids, Array
end
Dann, wenn die Werte für die Leser zuweisen, gelten Sie sie als
blog.reader_ids = [1,2,3,4]
Wenn Beziehungen auf diese Weise zuweisen, werden Duplikate automatisch entfernt.