ARel Mimic включает в себя с find_by_sql - PullRequest
11 голосов
/ 19 августа 2011

У меня есть довольно сложный SQL-запрос , который я почти уверен, что не смогу выполнить с ARel (Rails 3.0.10)

Проверьте ссылку, но у нее есть несколько объединений и предложение where exists, и я уверен, что это слишком сложно для ARel.

Однако моя проблема в том, что до того, как этот запрос был настолько сложным, с ARel я мог использовать includes для добавления других моделей, которые мне нужны, чтобы избежать проблем n + 1. Теперь, когда я использую find_by_sql, includes не работает. Я все еще хочу иметь возможность получать эти записи и прикреплять их к экземплярам моей модели, как это делает includes, но я не совсем уверен, как этого добиться.

Может ли кто-нибудь указать мне правильное направление?

Я еще не пытался объединить их в одном запросе. Я просто не уверен, как они будут сопоставлены с объектами (т. Е. Если ActiveRecord правильно сопоставит их с соответствующим классом)

Я знаю, что при использовании includes ActiveRecord фактически делает второй запрос, а затем каким-то образом прикрепляет эти строки к соответствующим экземплярам из исходного запроса. Может кто-нибудь наставить меня, как я могу это сделать? Или мне нужно присоединиться к одному и тому же запросу?

Ответы [ 3 ]

22 голосов
/ 29 февраля 2012

Давайте представим, что SQL действительно нельзя свести к Arel.Не все могут, и мы действительно хотим сохранить наш собственный find_by_sql, но мы также хотим использовать include.

Тогда 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 намного лучше, потому что вы можете применить загрузку в любое время.Таким образом, вы можете извлекать объекты в вашем контроллере, а затем непосредственно перед рендерингом вы можете проверить формат и загружать больше ассоциаций.Это то, что происходит со мной - HTML не нуждается в энергичной загрузке, но .json делает.

Это поможет?

1 голос
/ 25 августа 2011

@ pedrorolo, спасибо за хедз-ап на этом not exists запросе, помогли мне достичь того, что мне было нужно.Вот окончательное решение (они являются ключом .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
1 голос
/ 23 августа 2011

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

Проверьте это:

...