как избежать дубликатов в много:через отношения?
как я могу достичь следующих? У меня есть две модели (блоги и читатели) и таблица соединений, которая позволит мне иметь отношение 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
что я хочу сделать сейчас, это добавить читателей в разных блогах. Условие, однако, заключается в том, что я могу добавить читателя в блог только один раз. Поэтому не должно быть никаких дубликатов (same readerID
, то же самое blogID
) в BlogsReaders
таблица. Как я могу достичь этого?
второй вопрос: как я могу получить список блог что читатели еще не подписаны (например, для заполнения выпадающего списка выбора, который затем можно использовать для добавления читателя в другой блог)?
8 ответов
о:
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blog_ids])
Rails заботится о коллекции идентификаторов для нас с методами ассоциации! :)
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
Примечание добавление до has_many
звонок.
также вы можете рассмотреть has_and_belongs_to_many
между блогом и читателем, если у вас нет других атрибутов, которые вы хотели бы иметь в модели join (чего в настоящее время нет). Этот метод также имеет :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
обновление для 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
рельсы 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
ответ по этой ссылке показывает, как переопределить метод "идиома Rails, чтобы избежать дубликатов в has_many: через
Я думаю, что кто-то придет с лучшим ответом, чем это.
the_reader = Reader.find(:first, :include => :blogs)
Blog.find(:all,
:conditions => ['id NOT IN (?)', the_reader.blogs.map(&:id)])
[edit]
пожалуйста, смотрите ответ Джоша ниже. Так надо поступать. (Я знал, что там есть лучший выход ;)
верхний ответ в настоящее время говорит, чтобы использовать 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
самый простой способ-сериализовать отношение в массив:
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]
при назначении отношений таким образом дубликаты автоматически удаляются.