如何避免has_many:through关系中的重复?

Seb*_*ian 30 ruby ruby-on-rails duplicates has-many has-many-through

我怎样才能实现以下目标?我有两个模型(博客和读者)和一个JOIN表,它允许我在它们之间建立N:M关系:

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
Run Code Online (Sandbox Code Playgroud)

我现在想做的是将读者添加到不同的博客中.但是,条件是我只能将博客添加到博客中.因此表中不得有任何重复(相同readerID,相同blogID)BlogsReaders.我怎样才能做到这一点?

第二个问题是,如何获得读者尚未订阅的博客列表(例如,填写下拉选择列表,然后可以将读者添加到另一个博客)?

Ott*_*tto 82

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
Run Code Online (Sandbox Code Playgroud)

请注意将该:uniq => true选项添加到has_many呼叫中.

此外,您可能需要has_and_belongs_to_many在Blog和Reader之间考虑,除非您在连接模型上有一些其他属性(当前没有这些属性).那种方法也有一个:uniq选择.

请注意,这不会阻止您在表中创建条目,但它确实在您查询集合时只能获得每个对象中的一个.

更新

在Rails 4中,实现它的方法是通过范围块.以上更改为.

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
Run Code Online (Sandbox Code Playgroud)

Rails的更新5

使用uniq在范围块将导致错误 NoMethodError: undefined method 'extensions' for []:Array.distinct改为使用:

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
Run Code Online (Sandbox Code Playgroud)

  • 如果你有一个默认的范围来订购你的博客,你将需要取消它(或DISTINCT将失败),你可以使用:```has_many:blogs, - > {unscope(:order).uniq},通过: :blog_readers``` (3认同)
  • 我认为如果您的连接模型有任何其他字段,这种方法就会出现问题。例如,一个位置字段,以便每个子项都可以定位在其父项中。`blog.readers &lt;&lt; reader # blog_readers.position = 1;` `blog.readers &lt;&lt; reader # blog_readers.position = 2` 由于第二个 blog_readers 具有不同的位置,uniq 设置不会将其视为现有条目并允许它要被创建 (2认同)
  • 要更新@marksiemers,请使用以下方法来回答Rails 5.2的问题:has_many:blogs-&gt; {unscope(:order).distinct},方法是::blog_readers` (2认同)

Mik*_*een 37

这应该照顾你的第一个问题:

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

  validates_uniqueness_of :reader_id, :scope => :blog_id
end
Run Code Online (Sandbox Code Playgroud)

  • 请在此处仔细阅读有关并发性和完整性的信息 http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of (2认同)

pas*_*llo 17

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
Run Code Online (Sandbox Code Playgroud)

  • @pastullo但是它仍然将数据插入中间表blog_readers中。如何预防呢? (2认同)

Jos*_*man 5

关于什么:

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

Rails通过关联方法为我们收集ids!:)

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