2008-11-24 6 views
26

Как я могу достичь следующего? У меня есть две модели (блоги и читателей) и JOIN таблица, которая позволит мне иметь N: M отношения между ними:Как избежать дубликатов в has_many: через отношения?

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. Поэтому не должно быть никаких дубликатов (то же самое readerID, то же самое blogID) в таблице BlogsReaders. Как я могу это достичь?

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

ответ

5

насчет:

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

Rails заботится о коллекции идентификаторами для нас методами ассоциации! :)

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

+0

Кроме того, я хотел бы упомянуть, что это, вероятно, лучший метод, так как принятый ответ выбирает ВСЕ данные из строки (например, the_reader.blogs), тогда как мой ответ выбирает только идентификаторы из строк (например, the_reader. blog_ids). Это большой успех! – 2008-11-25 15:20:10

32

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

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

    validates_uniqueness_of :reader_id, :scope => :blog_id 
end 
+0

Я пытался выяснить это в течение долгого времени, и это никогда не происходило со мной! Отличное решение! Благодаря! – Arel 2013-09-11 16:29:17

+1

Пожалуйста, внимательно прочитайте о параллелизме и целостности здесь http://apidock.com/rails/ActiveRecord/Validations/ClassMethods/validates_uniqueness_of – 2016-11-11 20:28:26

1

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

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

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

[править]

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

+0

, вы также можете сделать это в одном заявлении, используя find_by_sql. – 2008-11-25 01:01:28

+0

Удивительный! Это прекрасно работает! Большое спасибо!! – Sebastian 2008-11-25 07:47:04

69

Simpler решение, которое встроено в 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 
0

Самый простой способ заключается в сериализации отношения в массив:

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] 

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

14

Рельсы 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 
0

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

 Смежные вопросы

  • Нет связанных вопросов^_^