2011-07-18 4 views
7

rails3 앱의 일부 범위를 기반으로 열심히 부하 연결을 시도했지만 해결책을 찾지 못했습니다. 따라서,rails3 범위 지정으로 Eagerloading

@projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries]) 

그것은 사용자의 모든 프로젝트를 가져옵니다 : 내가 코드를 다음 한

class Project 
has_many :entries 
has_many :to_dos 

class ToDo 
has_may :entries 
has_many :tasks 
belongs_to :project 

class Task 
has_many :entries 
belongs_to :to_do 

class Entry 
belongs_to :project 
belongs_to :to_do 
belongs_to :task 

# options format: {:from_date=>(Date.today-1.week), :to_date=>(Date.today+1.week), :user_id=>60} 
scope :filtered_list, lambda { |options| 
    condition = options[:user_id].nil? ? "true" : "user_id = #{options[:user_id]}" 
    condition += options[:from_date].nil? ? "" : " AND entry_date >= '#{options[:from_date]}'" 
    condition += options[:to_date].nil? ? "" : " AND entry_date <= '#{options[:to_date]}'" 
    where(condition) 
} 

그리고 프로젝트 # 지수는 사용자의 모든 프로젝트를 얻을 :

내 응용 프로그램은 다음과 같은 모델을 가지고 열정적으로 협회를 로딩했다. 그래서 다음 루프를 수행하여 프로젝트 내의 모든 항목을 가져 오면 새 쿼리가 시작되지 않습니다.

이 열렬한로드가 모든 항목을 가져 오므로, 실제로 필요한 것보다 너무 많은 데이터입니다. 그래서로드 된 항목에 몇 가지 조건을 적용하려고했지만 어떤 해결책도 찾을 수 없었습니다. 나는 다음과 같은 것을 찾고 있었다 :

@projects = current_user.projects.includes(:entries.filtered_list(options), :to_dos =>[:entries.filtered_list(options), :tasks => :entries.filtered_list(options)]) 

일부 조건을 만족하는 항목 만로드되도록.

eager loading으로 스코핑을 사용할 수 없습니까? 범위 지정과 함께 eagerloading을 사용하도록 도와주세요.

답변

1

내가 아는 한 범위는 이와 같은 포함 된 연결에 적용 할 수 없습니다. 그러나 열망하는로드 쿼리에만 적용해야하는 조건을 지정할 수 있습니다.

def self.filter_by(options) 
    condition = options[:user_id].nil? ? "true" : "entries.user_id = #{options[:user_id]}" 
    condition += options[:from_date].nil? ? "" : " AND entries.entry_date >= '#{options[:from_date]}'" 
    condition += options[:to_date].nil? ? "" : " AND entries.entry_date <= '#{options[:to_date]}' 
    condition 
end 

또는 좀 더 rubyesque : 다음

def self.filter_by(options) 
    conditions = [] 
    conditions << "entries.user_id = #{options[:user_id]}" unless options[:user_id].nil? 
    conditions << "entries.entry_date >= '#{options[:from_date]}'" unless options[:from_date].nil? 
    conditions << "entries.entry_date <= '#{options[:to_date]}'" unless options[:to_date].nil? 
    conditions.join(" AND ") 
end 

및 방법에 관한 것으로 체인 그래서 리팩토링의 비트와 함께, 당신은 당신이 현재 범위에 정의 된 조건을 생성하는 방법을 가질 수있다 당신의 열망로드 :

@projects = current_user.projects.includes(:entries, :to_dos =>[:entries, :tasks => :entries].where(Entry.filter_by(options)) 

또한 당신이 독립적으로 필요한 경우 범위에 재사용 :

,
scope :filtered_list, lambda { |options| where(Entry.filter_by(options)) } 

면책 조항 :이 내용은 실제 모델 정의로 테스트 한 것이 없지만 주위에 거짓말 한 것들과 잘 어울립니다.

또한 필터 옵션이 최종적으로 클라이언트 측에서 오는 경우 상태가 SQL 삽입에 취약하다는 점도 유의하십시오.

Rails는 JOIN을 사용하여 관련 데이터를로드하므로주의해야 할 사항입니다. 그것은 좋은 것 (질의가 적은 쿼리)이거나 나쁜 것 (인덱싱이 차선책 인 경우) 일 수 있습니다. 액티브 레코드가 있습니다

비록 당신이 단지 조인, 권장되는 방법은 대신 조인을 사용하는 것입니다 좋아하는 열망 로드 협회에 조건을 지정하십시오 guide이 말을하는 이유는 아마.