Как избежать дубликатов в 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
). Как я могу достичь этого?
Второй вопрос: как мне получить список блога, на который читатели уже не подписываются (например, чтобы заполнить список выпадающего списка, который затем можно использовать для добавления читателя в другой блог)?
Ответы
Ответ 1
Как насчет:
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blog_ids])
Rails заботится о коллекции идентификаторов для нас с помощью методов ассоциации!:)
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html
Ответ 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
Ответ 5
Ответ на эту ссылку показывает, как переопределить символ < < метод для достижения того, что вы ищете, не создавая исключений или создавая отдельный метод: Rails idiom, чтобы избежать дублирования в has_many: через
Ответ 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