38

どうすれば次のことを達成できますか? 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

私が今やりたいことは、さまざまなブログに読者を追加することです。ただし、読者をブログに追加できるのは 1 回だけという条件があります。したがって、テーブルに重複 (同じreaderID、同じblogID)があってはなりません。BlogsReadersどうすればこれを達成できますか?

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

4

9 に答える 9

93

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、結合モデルで使用したい他の属性がない限り (現在は使用していません)、Blog と Reader の間で検討することをお勧めします。そのメソッドにはオプションもあり:uniqます。

これにより、テーブルにエントリを作成できなくなるわけではありませんが、コレクションに対してクエリを実行すると、各オブジェクトが 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 []:Arraydistinct代わりに使用してください:

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
于 2008-11-25T17:13:19.373 に答える
39

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

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

  validates_uniqueness_of :reader_id, :scope => :blog_id
end
于 2008-11-24T23:08:27.787 に答える
20

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
于 2016-08-09T08:12:08.087 に答える
7

どうですか:

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

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

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

于 2008-11-25T14:39:59.840 に答える
2

このリンクの答えは、「<<」メソッドをオーバーライドして、例外を発生させたり、別のメソッドを作成したりせずに、探しているものを実現する方法を示しています: Rails idiom to avoid duplicates in has_many :through

于 2011-01-03T15:05:58.490 に答える
1

Rails 6では次のことを行います

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

  validates :blog_id, uniqueness: { scope: :reader_id }
end

一意性の違反を防ぐために、データベースの制約を作成することを忘れないでください。

于 2021-08-30T19:45:18.883 に答える
1

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

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

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

[編集]

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

于 2008-11-25T00:42:04.603 に答える
-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]

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

于 2016-02-02T22:32:42.163 に答える