Вопрос

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

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

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

Это было полезно?

Решение

Как насчет

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

Обратите внимание на добавление :uniq => true вариант для has_many позвони.

Также вы, возможно, захотите рассмотреть has_and_belongs_to_many между Blog и Reader, если только у вас нет каких-либо других атрибутов, которые вы хотели бы иметь в модели объединения (которых у вас в настоящее время нет).Этот метод также имеет :uniq опитон.

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

Обновить

В Rails 4 это можно сделать с помощью блока scope.Вышеуказанное изменяется на.

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

Способ 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

Ответ по этой ссылке показывает, как переопределить " < < " метод для достижения того, что вы ищете, без привлечения исключений или создания отдельного метода: Rails идиома, чтобы избежать дубликатов в has_many: через

Я думаю, что кто-нибудь найдет ответ получше, чем этот.

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

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

[править]

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

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

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

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]

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top