如何实现以下目标?我有两个模型(博客和读者)和一个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
我现在想做的是将读者添加到不同的博客中。但是,条件是我只能将博客添加到博客中。因此readerID
表中不得有任何重复项(相同blogID
,相同BlogsReaders
)。我怎样才能做到这一点?
第二个问题是,我如何获得读者尚未订阅的博客列表(例如填写下拉选择列表,然后可以将读者添加到另一个博客)?
答案 0 :(得分:77)
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
请注意将:uniq => true
选项添加到has_many
来电。
此外,您可能需要考虑Blog和Reader之间的has_and_belongs_to_many
,除非您在连接模型上有一些其他属性(当前没有)。该方法还有一个:uniq
opiton。
请注意,这不会阻止您在表中创建条目,但它确实在您查询集合时只能获得每个对象中的一个。
<强>更新强>
在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
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
答案 1 :(得分:36)
这应该照顾你的第一个问题:
class BlogsReaders < ActiveRecord::Base
belongs_to :blog
belongs_to :reader
validates_uniqueness_of :reader_id, :scope => :blog_id
end
答案 2 :(得分: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
答案 3 :(得分:5)
怎么样:
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blog_ids])
Rails通过关联方法为我们收集id! :)
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
答案 4 :(得分:2)
此链接的答案显示了如何覆盖“&lt;&lt;”在不引发异常或创建单独方法的情况下实现您所需要的方法:Rails idiom to avoid duplicates in has_many :through
答案 5 :(得分:1)
我认为有人会得到比这更好的答案。
the_reader = Reader.find(:first, :include => :blogs)
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])
[编辑]
请参阅下面的Josh的回答。这是要走的路。 (我知道有更好的方法;)
答案 6 :(得分:1)
目前最常见的答案是在proc中使用uniq
:
class Blog < ActiveRecord::Base
has_many :blogs_readers, dependent: :destroy
has_many :readers, -> { uniq }, through: :blogs_readers
end
然而,这会将关系踢入一个数组,并且可以破坏期望对关系而不是数组执行操作的事物。
如果您使用distinct
,则会将其保留为关系:
class Blog < ActiveRecord::Base
has_many :blogs_readers, dependent: :destroy
has_many :readers, -> { distinct }, through: :blogs_readers
end
答案 7 :(得分:-1)
最简单的方法是将关系序列化为数组:
class Blog < ActiveRecord::Base
has_many :blogs_readers, :dependent => :destroy
has_many :readers, :through => :blogs_readers
serialize :reader_ids, Array
end
然后在为读者分配值时,将它们应用为
blog.reader_ids = [1,2,3,4]
以这种方式分配关系时,会自动删除重复项。