Подтвердить что ты не робот

Как избежать дубликатов в has_many: через отношения?

Как я могу добиться следующего? У меня две модели (блоги и читатели) и таблица 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

Что я хочу сделать сейчас, это добавить читателей в разные блоги. Условие, однако, состоит в том, что я могу добавить читателя в блог ONCE. Таким образом, в таблице BlogsReaders не должно быть никаких дубликатов (те же readerID, одинаковые blogID). Как я могу достичь этого?

Второй вопрос: как мне получить список блога, на который читатели уже не подписываются (например, чтобы заполнить список выпадающего списка, который затем можно использовать для добавления читателя в другой блог)?

4b9b3361

Ответ 2

Упрощенное решение, встроенное в 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.

Обратите внимание, что это не мешает вам создавать записи в таблице, но гарантирует, что при запросе коллекции вы получаете только один из каждого объекта.

Обновление

В 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

Ответ 3

Это должно позаботиться о вашем первом вопросе:

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

  validates_uniqueness_of :reader_id, :scope => :blog_id
end

Ответ 4

Путь 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

Ответ 6

Я думаю, что кто-то придет с лучшим ответом, чем это.

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

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

[править]

См. ниже отчет Джоша. Это путь. (Я знал, что там был лучший способ;)

Ответ 7

Самый простой способ - сериализовать взаимосвязь в массиве:

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]

При назначении отношений таким образом дубликаты автоматически удаляются.

Ответ 8

В верхнем ответе в настоящее время говорится использовать 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