質問

以下を達成するにはどうすればよいですか? 2つのモデル(ブログとリーダー)と、それらの間にN:Mの関係を持たせることができるJOINテーブルがあります:

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)があってはなりません。どうすればこれを達成できますか?

2番目の質問は、読者がまだ購読していないブログのリストを取得する方法です(たとえば、ドロップダウン選択リストに入力して、読者を別のブログに追加するために使用できます)。

役に立ちましたか?

解決

概要:

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

Railsは、関連付けメソッドを使用してIDのコレクションを処理します! :)

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

他のヒント

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オプションを追加することに注意してください。

また、ブログとリーダーの間でhas_and_belongs_to_manyを検討することもできます。ただし、結合モデルにその他の属性を持たせたい場合(現在はそうではありません)。このメソッドには:uniq opitonもあります。

これは、テーブルにエントリを作成することを妨げるわけではありませんが、コレクションをクエリすると、各オブジェクトが1つだけ取得されることに注意してください。

更新

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

これで最初の質問が処理されます:

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

  validates_uniqueness_of :reader_id, :scope => :blog_id
end

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

このリンクの回答は、<!> quot; <!> lt; <!> lt; <!> quot;をオーバーライドする方法を示しています。例外を発生させたり、別のメソッドを作成したりすることなく、探しているものを実現する方法: hails_manyでの重複を避けるためのレールイディオム:through

誰かがこれよりも良い答えを出すと思います。

the_reader = Reader.find(:first, :include => :blogs)

Blog.find(:all, 
          :conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])

[編集]

以下のジョシュの答えをご覧ください。それは行く方法です。 (そこにもっと良い方法があることは知っていました;)

現在、一番の答えは、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

最も簡単な方法は、関係を配列にシリアル化することです:

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]

この方法で関係を割り当てると、重複は自動的に削除されます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top