Powiedzmy, że mam następujące klasy
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
ma zakres life_supporting
i SolarSystem
has_many :planets
. Chciałbym zdefiniować moją relację has_many, aby gdy pytam solar_system
o wszystkie powiązane planets
, life_supporting
zakres jest stosowany automatycznie. Zasadniczo chciałbym solar_system.planets == solar_system.planets.life_supporting
.
Wymagania
Ja nie chce zmienić
scope :life_supporting
wPlanet
celudefault_scope where('distance_from_sun > ?', 5).order('diameter ASC')
Chciałbym również zapobiec powielaniu, ponieważ nie muszę nic dodawać
SolarSystem
has_many :planets, :conditions => ['distance_from_sun > ?', 5], :order => 'diameter ASC'
Cel
Chciałbym mieć coś takiego
has_many :planets, :with_scope => :life_supporting
Edycja: praca wokół
Jak powiedział @phoet, osiągnięcie domyślnego zakresu przy użyciu ActiveRecord może nie być możliwe. Jednak znalazłem dwa potencjalne obejścia. Oba zapobiegają powielaniu. Pierwsza, choć długa, zachowuje oczywistą czytelność i przejrzystość, a druga jest metodą pomocniczą, której wynik jest wyraźny.
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
Innym rozwiązaniem, które jest dużo czystsze, jest po prostu dodanie następującej metody do SolarSystem
def life_supporting_planets
planets.life_supporting
end
i używać solar_system.life_supporting_planets
wszędzie tam, gdzie chceszsolar_system.planets
.
Żadne z nich nie odpowiada na pytanie, więc po prostu umieszczam je tutaj, aby obejść tę sytuację, gdyby ktoś inny napotkał taką sytuację.
where_values
może nie działać z warunkami mieszania:{:cleared => false}
... daje tablicę skrótów, których ActiveRecord nie lubi. Jako hack, przechwycenie pierwszego elementu w tablicy działa:Planet.life_supporting.where_values[0]
...where_ast
zamiastwhere_values
lubwhere_values_hash
tak, jak użyłem AREL w lunecie w innym modelu. Udało się! +1Odpowiedzi:
W Railsach 4
Associations
masz opcjonalnyscope
parametr, który akceptuje lambdę, która jest stosowana doRelation
(por. Dokumentacja 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
W Railsach 3
where_values
obejście można czasami poprawić, używającwhere_values_hash
tego, który obsługuje lepsze zakresy, w których warunki są definiowane przez wielokrotnośćwhere
lub przez hash (nie ma to miejsca w tym przypadku).has_many :planets, conditions: Planet.life_supporting.where_values_hash
źródło
has_many :planets, conditions: Planet.life_supporting.where_values_hash
aby wymusić zakres. Jest to również złote dla chętnego ładowania.where_values_hash
nie działa to z tekstem, w którym klauzule, np.User.where(name: 'joe').where_values_hash
Zwrócą hash oczekiwanych warunków, podczas gdyUser.where('name = ?', 'Joe').where_values_hash
nie. Dlatego przykład planet prawdopodobnie zawiedzie.W Railsach 5 następujący kod działa dobrze ...
class Order scope :paid, -> { where status: %w[paid refunded] } end class Store has_many :paid_orders, -> { paid }, class_name: 'Order' end
źródło
Właśnie zagłębiłem się w ActiveRecord i nie wygląda na to, że można to osiągnąć przy obecnej implementacji
has_many
. możesz przejść blok do:conditions
ale jest to ograniczone do zwrócenia skrótu warunków, a nie jakichkolwiek rzeczy związanych z arelami.naprawdę prostym i przejrzystym sposobem osiągnięcia tego, co chcesz (co myślę, że próbujesz zrobić) jest zastosowanie zakresu w czasie wykonywania:
# foo.rb def bars super.baz end
to jest dalekie od tego, o co prosisz, ale może po prostu zadziałać;)
źródło
has_many
deklaracji, ponieważ jest to wyraźniejsze.