Mam tablicę obiektów, nazwijmy to Indicator
. Chcę uruchomić metody klasy Indicator (te z def self.subjects
różnych, zakresów itp.) Na tej tablicy. Jedynym sposobem, w jaki znam, aby uruchamiać metody klas na grupie obiektów, jest posiadanie ich jako ActiveRecord :: Relation. W końcu uciekam się do dodania to_indicators
metody do Array
.
def to_indicators
# TODO: Make this less terrible.
Indicator.where id: self.pluck(:id)
end
Czasami łączę kilka z tych zakresów w celu odfiltrowania wyników w ramach metod klasowych. Tak więc, mimo że wywołuję metodę na ActiveRecord :: Relation, nie wiem, jak uzyskać dostęp do tego obiektu. Do jego zawartości mogę dotrzeć tylko przez all
. Ale all
jest tablicą. Więc muszę przekonwertować tę tablicę na ActiveRecord :: Relation. Na przykład jest to część jednej z metod:
all.to_indicators.applicable_for_bank(id).each do |indicator|
total += indicator.residual_risk_for(id)
indicator_count += 1 if indicator.completed_by?(id)
end
Myślę, że to sprowadza się do dwóch pytań.
- Jak mogę przekonwertować Array obiektów na ActiveRecord :: Relation? Najlepiej bez robienia za
where
każdym razem. - Podczas uruchamiania
def self.subjects
metody typu na ActiveRecord :: Relation, w jaki sposób mogę uzyskać dostęp do samego obiektu ActiveRecord :: Relation?
Dzięki. Jeśli muszę coś wyjaśnić, daj mi znać.
źródło
.all
, po prostu użyj.scoped
tak, jak wskazuje odpowiedź Andrew Marshalla (Chociaż w rails 4 będzie działać.all
). Jeśli okaże się, że musisz zamienić tablicę w relację, gdzieś popełniłeś błąd ...Odpowiedzi:
Nie można przekonwertować Array na ActiveRecord :: Relation, ponieważ Relation jest tylko konstruktorem zapytania SQL, a jego metody nie działają na rzeczywistych danych.
Jeśli jednak chcesz mieć relację, to:
dla ActiveRecord 3.x, nie wywołuj
all
i zamiast tego wywołajscoped
, co zwróci relację, która reprezentuje te same rekordy,all
które dałyby ci w Array.dla ActiveRecord 4.x, po prostu wywołaj
all
, co zwróci Relację.Gdy metoda jest wywoływana na obiekcie Relation,
self
jest to relacja (w przeciwieństwie do klasy modelu, w której jest zdefiniowana).źródło
class User; def self.somewhere; where(location: "somewhere"); end; end
, a następnieUser.limit(5).somewhere
Możesz przekonwertować tablicę obiektów
arr
na ActiveRecord :: Relation w ten sposób (zakładając, że wiesz, jaką klasą są obiekty, co prawdopodobnie robisz)Musisz jednak użyć
where
, jest to przydatne narzędzie, którego nie powinieneś niechętnie używać. Teraz masz jednowierszowy konwertujący tablicę na relację.map(&:id)
zamieni twoją tablicę obiektów w tablicę zawierającą tylko ich identyfikatory. A przekazanie tablicy do klauzuli where wygeneruje instrukcję SQL,IN
która wygląda mniej więcej tak:Pamiętaj, że uporządkowanie tablicy zostanie utracone - ale ponieważ Twoim celem jest tylko uruchomienie metody klasy na kolekcji tych obiektów, zakładam, że nie będzie to problem.
źródło
where(id: arr.map(&:id))
? Ściśle mówiąc, nie konwertuje to tablicy na relację, ale zamiast tego pobiera nowe instancje obiektów (po zrealizowaniu relacji) z tymi identyfikatorami, które mogą mieć inne wartości atrybutów niż instancje znajdujące się już w pamięci w tablicy.Cóż, w moim przypadku muszę przekonwertować tablicę obiektów na ActiveRecord :: Relation, a także posortować je według określonej kolumny (na przykład id). Ponieważ używam MySQL, funkcja pola może być pomocna.
SQL wygląda następująco:
Funkcja pola MySQL
źródło
ActiveRecord::Relation
wiąże zapytanie bazy danych, które pobiera dane z bazy danych.Załóżmy, że ma sens, mamy tablicę z obiektami tej samej klasy, a następnie z jakim zapytaniem mamy je powiązać?
Kiedy biegnę,
Tutaj, powyżej,
users
zwróćRelation
obiekt, ale wiąże zapytanie bazy danych za nim i możesz go wyświetlić,Nie ma więc możliwości powrotu
ActiveRecord::Relation
z tablicy obiektów, która jest niezależna od zapytania sql.źródło
Przede wszystkim NIE jest to srebrna kula. Z mojego doświadczenia odkryłem, że konwersja do relacji jest czasami łatwiejsza niż alternatywy. Staram się stosować to podejście bardzo oszczędnie i tylko w przypadkach, gdy alternatywa byłaby bardziej złożona.
Biorąc to pod uwagę, jest to moje rozwiązanie, rozszerzyłem
Array
klasęPrzykładem użycia może być
array.to_activerecord_relation.update_all(status: 'finished')
. Gdzie go teraz użyć?Czasami trzeba odfiltrować,
ActiveRecord::Relation
na przykład wyjąć nieukończone elementy. W takich przypadkach najlepiej jest użyć zasięguelements.not_finished
i nadal zachowaćActiveRecord::Relation
.Ale czasami ten stan jest bardziej złożony. Wyjmij wszystkie elementy, które nie są ukończone, a które zostały wyprodukowane w ciągu ostatnich 4 tygodni i zostały sprawdzone. Aby uniknąć tworzenia nowych zakresów, możesz filtrować do tablicy, a następnie konwertować z powrotem. Pamiętaj, że nadal wykonujesz zapytanie do DB, szybko, ponieważ wyszukuje on,
id
ale nadal jest zapytaniem.źródło