Атрибут AREL включает в себя: find_by_sql
У меня есть довольно сложный sql-запрос, который, я уверен, я не могу выполнить с AREL (Rails 3.0.10)
Проверьте ссылку, но она имеет несколько объединений и предложение where exists
, и я уверен, что это слишком сложно для AREL.
Моя проблема заключается в том, что до того, как этот запрос был настолько сложным, с AREL я мог бы использовать includes
, чтобы добавить другие модели, которые мне нужны, чтобы избежать n + 1 проблем. Теперь, когда я использую find_by_sql, includes
не работает. Я все еще хочу иметь возможность извлекать эти записи и присоединять их к экземплярам модели, как это делает includes
, но я не совсем уверен, как этого добиться.
Может ли кто-нибудь указать мне в правильном направлении?
Я еще не пробовал присоединиться к ним в том же запросе. Я просто не уверен, как они будут сопоставлены с объектами (т.е. Если ActiveRecord правильно сопоставляет их с соответствующим классом)
Я знаю, что при использовании includes
ActiveRecord фактически выполняет второй запрос, затем каким-то образом привязывает эти строки к соответствующим экземплярам из исходного запроса. Может ли кто-нибудь научить меня, как я могу это сделать? Или мне нужно присоединиться к одному и тому же запросу?
Ответы
Ответ 1
Предположим, что SQL действительно не может быть сведена к Arel. Не все может, и нам действительно очень хочется сохранить наш пользовательский find_by_sql, но мы также хотим использовать его.
Затем preload_associations - ваш друг:
(Обновлено для Rails 3.1)
class Person
def self.custom_query
friends_and_family = find_by_sql("SELECT * FROM people")
# Rails 3.0 and lower use this:
# preload_associations(friends_and_family, [:car, :kids])
# Rails 3.1 and higher use this:
ActiveRecord::Associations::Preloader.new(friends_and_family, [:car, :kids]).run
friends_and_family
end
end
Обратите внимание, что метод 3.1 намного лучше, b/c вы можете применять загрузку в любое время. Таким образом, вы можете получить объекты в своем контроллере, а затем перед рендерингом вы можете проверить формат и загружать больше ассоциаций. То, что происходит для меня - html не нуждается в активной загрузке, но .json делает.
Это поможет?
Ответ 2
Я уверен, что вы можете делать даже самые сложные запросы с Arel. Может быть, вы слишком скептически относитесь к этому.
Отметьте:
Ответ 3
@pedrorolo благодарит за хэдз-ап за этот запрос not exists
isl, помог мне добиться того, что мне нужно. Здесь окончательное решение (они являются окончательными .exists
в запросе GroupChallenge:
class GroupChallenge < ActiveRecord::Base
belongs_to :group
belongs_to :challenge
def self.challenges_for_contact(contact_id, group_id=nil)
group_challenges = GroupChallenge.arel_table
group_contacts = GroupContact.arel_table
challenges = Challenge.arel_table
groups = Group.arel_table
query = group_challenges.project(1).
join(group_contacts).on(group_contacts[:group_id].eq(group_challenges[:group_id])).
where(group_challenges[:challenge_id].eq(challenges[:id])).
where(group_challenges[:restrict_participants].eq(true)).
where(group_contacts[:contact_id].eq(contact_id))
query = query.join(groups).on(groups[:id].eq(group_challenges[:group_id])).where(groups[:id].eq(group_id)) if group_id
query
end
end
class Challenge < ActiveRecord::Base
def self.open_for_participant(contact_id, group_id = nil)
open.
joins("LEFT OUTER JOIN challenge_participants as cp ON challenges.id = cp.challenge_id AND cp.contact_id = #{contact_id.to_i}").
where(['cp.accepted != ? or cp.accepted IS NULL', false]).
where(GroupChallenge.challenges_for_contact(contact_id, group_id).exists.or(table[:open_to_all].eq(true)))
end
end