2010-12-15 3 views
0

У меня есть 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 

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

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

Спасибо.

ответ

0

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

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

Вот еще один способ, которым Вы могли бы сделать это:

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, и на этот раз включить всех авторов, связанных с этими книгами, и кэшировать их, чтобы избежать ненужных вызовов db.

Edit:

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

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

Спасибо, я подумал об этом решении, но я надеялся, что есть более простое решение. Что касается объединений: когда я использую объединения в этом примере, я бы получил массив из 3 книг, которые являются одной и той же книгой, каждая из которых имеет другого автора, поэтому она не решает проблему ... – Nadav

+0

Хорошо, когда вы говорите это Например, я предполагаю, что вы имеете в виду, когда вы просто меняете: include to: join в описанном вами примере. Потому что я проверил код, который я поставил последним, и это сработало для меня. – DanneManne

+0

Да, ваш код работает, но я не хочу разбить его в коде на 2 разных запроса. Разве нет способа запуска одного запроса на рельсы без извлечения идентификаторов вручную, а затем для извлечения самих объектов? – Nadav