Ruby on Rails: find не получает все объекты, связанные при использовании условий - PullRequest
0 голосов
/ 15 декабря 2010

У меня есть 2 модели с отношением «многие ко многим» (здесь это упрощено на примере авторов книг):

class Book < ActiveRecord::Base  
    has_and_belongs_to_many :authors
end

class Author < ActiveRecord::Base
    has_and_belongs_to_many :books
end

Мне нужно отобразить список книг, а под каждой - списокего авторы.Пользователи могут фильтровать по определенному автору.

Я пытаюсь отфильтровать по автору следующим образом:
Предположим, есть только одна полученная книга и у нее 3 автора.

books = Book.find(:all, :include => :authors, :conditions => "authors.id = 5")

Когда я пытаюсь составить список различных авторов найденных книг, я получаю только автора с ID = 5.

books.first.authors.size   # => 1
books.first.authors.first.id   # => 5

Я ожидаю, что рельсы пойдут и найдут всех разных авторов.связан с книгами, либо во время поиска, либо в качестве последующего запроса, но это не так.

Как я могу решить эту проблему?Что-то не так с ассоциациями?запрос на поиск?

Спасибо.

1 Ответ

0 голосов
/ 15 декабря 2010

Это происходит потому, что вы используете: включите в свой метод поиска. Когда вы делаете это, вы указываете Rails кэшировать связанный результат (жадную загрузку), чтобы избежать множественных запросов к базе данных. А поскольку указанные вами условия включают в себя только одного из трех авторов, он будет единственным, который будет кэшироваться и затем циклически повторяться при вызове .authors

Я думаю, что самым простым решением было бы изменить: включить в: объединения. Он выполнит тот же запрос к базе данных, но не будет кэшировать связанных авторов. Поэтому, когда вы вызываете авторов для каждой книги, она выполняет новый вызов базы данных, чтобы найти всех авторов для этой книги. Как я уже сказал, самый простой, но, возможно, не самый лучший, поскольку это может привести к большому количеству запросов к базе данных.

Вот еще один способ сделать это:

book_ids = Book.all(:joins => :authors, 
                    :conditions => ["authors.id = ?", 5]).map(&:id)
books = Book.all(:include => :authors, 
                 :conditions => ["books.id IN (?)", book_ids])

Сначала будут собраны все идентификаторы книг, связанных с конкретным автором. Затем он снова запросит базу данных с полученными значениями book_ids и на этот раз включит всех авторов, связанных с этими книгами, и кеширует ее, чтобы избежать ненужных вызовов БД.

Редактировать :

Если вы действительно хотите использовать только один вызов, то я думаю, что единственные варианты - это написать немного больше SQL в вызове. Возможно так:

books = Book.all(:include => :authors, 
                 :conditions => ["books.id IN (SELECT book_id FROM authors_books WHERE author_id = ?)", 5])
...