Rails has_many 관계에서 기본적으로 범위 사용


81

다음과 같은 수업이 있다고 가정 해 봅시다.

class SolarSystem < ActiveRecord::Base
  has_many :planets
end

class Planet < ActiveRecord::Base
  scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC')
end

Planet범위 life_supportingSolarSystem has_many :planets. 내가 물어 때 그래서 난 내 has_many 관계를 정의하고자하는 solar_system모든 관련 위해 planetslife_supporting범위가 자동으로 적용됩니다. 기본적으로 solar_system.planets == solar_system.planets.life_supporting.

요구 사항

  • 나는 할 수 없습니다 변경할 scope :life_supportingPlanet

    default_scope where('distance_from_sun > ?', 5).order('diameter ASC')

  • 또한 추가 할 필요없이 중복을 방지하고 싶습니다. SolarSystem

    has_many :planets, :conditions => ['distance_from_sun > ?', 5], :order => 'diameter ASC'

나는 같은 것을 갖고 싶다

has_many :planets, :with_scope => :life_supporting

편집 : 해결 방법

@phoet이 말했듯이 ActiveRecord를 사용하여 기본 범위를 달성하지 못할 수도 있습니다. 그러나 두 가지 잠재적 인 해결 방법을 찾았습니다. 둘 다 중복을 방지합니다. 첫 번째는 길지만 분명한 가독성과 투명성을 유지하고 두 번째는 출력이 명시적인 도우미 유형 메서드입니다.

class SolarSystem < ActiveRecord::Base
  has_many :planets, :conditions => Planet.life_supporting.where_values,
    :order => Planet.life_supporting.order_values
end

class Planet < ActiveRecord::Base
  scope :life_supporting, where('distance_from_sun > ?', 5).order('diameter ASC')
end

훨씬 더 깨끗한 또 다른 해결책은 다음 방법을 추가하는 것입니다. SolarSystem

def life_supporting_planets
  planets.life_supporting
end

그리고 solar_system.life_supporting_planets어디에서나 사용할 수 있습니다 solar_system.planets.

둘 다 질문에 대답하지 않으므로 다른 사람이 이러한 상황에 직면 할 경우 해결 방법으로 여기에 넣었습니다.


2
where_vales를 사용하여 해결 방법은 정말 최고의 가용성 솔루션과 가치가 수락 된 대답이다
빅토르 트론

where_values해시 조건에서 작동하지 않을 수 있습니다. {:cleared => false}... ActiveRecord가 좋아하지 않는 해시 배열을 제공합니다. 해킹으로 배열의 첫 번째 항목을 잡는 것은 작동합니다. Planet.life_supporting.where_values[0]...
Nolan Amy

나는 내가 사용했던 발견 where_ast보다는 where_values또는 where_values_hash내가 다른 모델의 범위에 AREL을 사용했다한다. 치료를했습니다! +1
br3nt 2015-06-11

답변:


130

Rails 4 Associations에는에 scope적용되는 람다를 허용 하는 선택적 매개 변수가 있습니다 Relation(참조 : ActiveRecord :: Associations :: ClassMethods에 대한 문서 ).

class SolarSystem < ActiveRecord::Base
  has_many :planets, -> { life_supporting }
end

class Planet < ActiveRecord::Base
  scope :life_supporting, -> { where('distance_from_sun > ?', 5).order('diameter ASC') }
end

Rails 3 에서는 조건이 다중 또는 해시 (여기에서는 해당되지 않음) 로 정의되는 더 나은 범위를 처리 하는 where_values방법을 사용하여 해결 방법을 개선 할 수 있습니다 .where_values_hashwhere

has_many :planets, conditions: Planet.life_supporting.where_values_hash

Rails 3 솔루션에 대해 자세히 설명해 주시겠습니까? 레일 4 하나가 너무 깨끗합니다!
Augustin Riedinger 2014 년

1
Rails 3의 has_many :planets, conditions: Planet.life_supporting.where_values_hash경우 범위를 강화하기 위해 읽어야 합니다. 이것은 또한 eager loading에 황금색입니다.
신경 학자

1
예를 들어 예상 조건의 해시를 반환하는 반면 그렇지 않은 경우 절이 where_values_hash텍스트 에서 작동하지 않는 어려운 방법을 발견했습니다 . 따라서 행성의 예는 실패 할 가능성이 높습니다. User.where(name: 'joe').where_values_hashUser.where('name = ?', 'Joe').where_values_hash
신경 학자

1
@nerfologist 마지막 코드 예제에서 실수를 지적 해 주셔서 감사합니다. 답을 편집했습니다. 두 번째 의견은 의미가 있습니다. 답변의 마지막 단락에서 제가 언급 한 내용이라고 생각합니다. 한계를 설명하는 더 명확한 방법을 찾을 수 있다면 내 대답을 자유롭게 편집하십시오.
user1003545

2
GrégoireClermont @이 더 이상 5 레일에서 작업하지 않습니다
elquimista

16

Rails 5에서는 다음 코드가 잘 작동합니다.

  class Order 
    scope :paid, -> { where status: %w[paid refunded] }
  end 

  class Store 
    has_many :paid_orders, -> { paid }, class_name: 'Order'
  end 

1

방금 ActiveRecord에 대해 자세히 살펴 보았고 현재 .NET 구현으로 이것이 달성 될 수있는 것처럼 보이지 않습니다 has_many. 당신은 블록을 전달할 수 :conditions있지만 이것은 어떤 종류의 영역 물건이 아닌 조건의 해시를 반환하는 것으로 제한됩니다.

원하는 것을 달성하는 정말 간단하고 투명한 방법 (내가하려는 작업)은 런타임에 범위를 적용하는 것입니다.

  # foo.rb
  def bars
    super.baz
  end

이것은 당신이 요구하는 것과는 거리가 멀지 만 작동 할 수도 있습니다.)


감사합니다 phoet! 이 , 작동하지만 그것은 단지 코드 리뷰에 이상한 약간을 볼 것이다. 이것을 구현할 때 얻을 수있는 피드백은 has_many보다 명확하기 때문에 선언 에 대한 중복을 대신 수행하는 것입니다.
Aaron

코드 검토의 관점에서 볼 때이 옵션을 조건의 중복보다 선호합니다. 의도 된 작업에 대한 테스트를 제공하는 한이 방법은 훨씬 더 DRY 및 SRP입니다
phoet

일반적으로 연관이 사용되는 경우 이와 같은 방법을 사용하지 않는 것이 좋습니다. 대신 연결에서 명시 적으로 호출되는 연결 및 범위에서 조건을 제거합니다. 앞으로 더 유지 관리가 쉽고 명확해질 것입니다.
BM5k 2013

죄송합니다 . 이 게시물이 정말 오래 되었다는 것을 깨달았습니다 . 비슷한 문제를 조사하는 중입니다.
BM5k 2013
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.