has_many의 복제를 피하는 방법 : 관계를 통해?
-
11-07-2019 - |
문제
다음을 어떻게 달성 할 수 있습니까? 나는 두 가지 모델 (블로그와 독자)과 조인 테이블이있어서 그들 사이에 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
테이블. 이것을 어떻게 달성 할 수 있습니까?
두 번째 질문은 독자가 이미 구독하지 않은 블로그 목록을 얻는 방법입니다 (예 : 드롭 다운 선택 목록을 채우고 다른 블로그에 독자를 추가하는 데 사용할 수 있는가)?
해결책
는 어때:
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blog_ids])
Rails는 협회 방법으로 우리를위한 ID 모음을 처리합니다! :)
http://api.rubyonrails.org/classes/activerecord/associations/classmethods.html
다른 팁
레일에 내장 된 더 간단한 솔루션 :
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
전화.
또한 고려하고 싶을 수도 있습니다 has_and_belongs_to_many
블로그와 독자 사이에, 가입 모델에 가고 싶은 다른 속성이 없다면 (현재하지 않는). 그 방법에는 a가 있습니다 :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
레일에 대한 업데이트 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
이것은 첫 번째 질문을 처리해야합니다.
class BlogsReaders < ActiveRecord::Base
belongs_to :blog
belongs_to :reader
validates_uniqueness_of :reader_id, :scope => :blog_id
end
레일 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
이 링크의 답변은 예외를 제기하거나 별도의 메소드를 만들지 않고 원하는 것을 달성하기 위해 "<<"메소드를 무시하는 방법을 보여줍니다. Has_many의 복제를 피하려는 Rails 관용구 :
나는 누군가가 이것보다 더 나은 대답을 함께 올 것이라고 생각합니다.
the_reader = Reader.find(:first, :include => :blogs)
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])
편집하다
아래 Josh의 답변을 참조하십시오. 가는 길입니다. (나는 거기에 더 나은 방법이 있다는 것을 알고 있었다;)
최고 답변은 현재 사용한다고합니다 uniq
Proc에서 :
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
가장 쉬운 방법은 관계를 배열로 연속화하는 것입니다.
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]
이런 방식으로 관계를 할당 할 때 중복은 자동으로 제거됩니다.