Ruby on Rails
Interfejs zapytań ActiveRecord
Szukaj…
Wprowadzenie
ActiveRecord to M in MVC, który jest warstwą systemu odpowiedzialną za reprezentowanie danych biznesowych i logiki. Techniką łączenia obiektów bogatych aplikacji z tabelami w systemie zarządzania relacyjnymi bazami danych jest O rject R elational M apper ( ORM ).
ActiveRecord wykona dla ciebie zapytania w bazie danych i jest kompatybilny z większością systemów baz danych. Niezależnie od używanego systemu bazy danych format metody ActiveRecord zawsze będzie taki sam.
.gdzie
Metoda where
jest dostępna w dowolnym modelu ActiveRecord
i umożliwia odpytywanie bazy danych o zestaw rekordów pasujących do podanych kryteriów.
Metoda where
akceptuje skrót, w którym klucze odpowiadają nazwom kolumn w tabeli reprezentowanej przez model.
Jako prosty przykład użyjemy następującego modelu:
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
end
Aby znaleźć wszystkie osoby o imieniu Sven
:
people = Person.where(first_name: 'Sven')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven'"
Aby znaleźć wszystkie osoby o imieniu Sven
i nazwisku Schrodinger
:
people = Person.where(first_name: 'Sven', last_name: 'Schrodinger')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven' AND last_name='Schrodinger'"
W powyższym przykładzie dane wyjściowe sql pokazują, że rekordy zostaną zwrócone tylko wtedy, gdy zarówno first_name
i last_name
zgodne.
zapytanie z warunkiem LUB
Aby znaleźć rekordy za pomocą first_name == 'Bruce'
LUB last_name == 'Wayne'
User.where('first_name = ? or last_name = ?', 'Bruce', 'Wayne')
# SELECT "users".* FROM "users" WHERE (first_name = 'Bruce' or last_name = 'Wayne')
. gdzie z tablicą
Metodę where
w dowolnym modelu ActiveRecord można użyć do wygenerowania kodu SQL w postaci WHERE column_name IN (a, b, c, ...)
. Osiąga się to poprzez przekazanie tablicy jako argumentu.
Jako prosty przykład użyjemy następującego modelu:
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
end
people = Person.where(first_name: ['Mark', 'Mary'])
people.to_sql # "SELECT * FROM people WHERE first_name IN ('Mark', 'Mary')"
Jeśli tablica zawiera nil
, SQL zostanie zmodyfikowany, aby sprawdzić, czy kolumna ma null
:
people = Person.where(first_name: ['Mark', 'Mary', nil])
people.to_sql # "SELECT * FROM people WHERE first_name IN ('Mark', 'Mary') OR first_name IS NULL"
Zakresy
Zakresy działają jak predefiniowane filtry w modelach ActiveRecord
.
Zakres jest definiowany przy użyciu metody klasy scope
.
Jako prosty przykład użyjemy następującego modelu:
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
#attribute :age, :integer
# define a scope to get all people under 17
scope :minors, -> { where(age: 0..17) }
# define a scope to search a person by last name
scope :with_last_name, ->(name) { where(last_name: name) }
end
Zakresy można wywoływać bezpośrednio z klasy modelu:
minors = Person.minors
Lunety mogą być powiązane:
peters_children = Person.minors.with_last_name('Peters')
Można również połączyć metodę where
i inne metody typu zapytania:
mary_smith = Person.with_last_name('Smith').where(first_name: 'Mary')
Za kulisami zakresy są po prostu cukrem syntaktycznym dla standardowej metody klasowej. Na przykład te metody są funkcjonalnie identyczne:
scope :with_last_name, ->(name) { where(name: name) }
# This ^ is the same as this:
def self.with_last_name(name)
where(name: name)
end
Domyślny zakres
w swoim modelu, aby ustawić domyślny zakres dla wszystkich operacji na modelu.
Jest jeden zauważalną różnicą pomiędzy
scope
sposobu i metody klasy:scope
-defined zakresy zawsze zwróciActiveRecord::Relation
, nawet jeśli logika wewnątrz zwrotów zerowe. Metody klasowe nie mają jednak takiej siatki bezpieczeństwa i mogą przerwać łańcuch, jeśli zwrócą coś innego.
gdzie nie
where
klauzule można negować za pomocą składni where.not
:
class Person < ApplicationRecord #attribute :first_name, :string end people = Person.where.not(first_name: ['Mark', 'Mary']) # => SELECT "people".* FROM "people" WHERE "people"."first_name" NOT IN ('Mark', 'Mary')
Obsługiwane przez ActiveRecord 4.0 i nowsze wersje.
Zamawianie
Możesz zamówić wyniki zapytań ActiveRecord za pomocą .order
:
User.order(:created_at)
#=> => [#<User id: 2, created_at: "2015-08-12 21:36:23">, #<User id: 11, created_at: "2015-08-15 10:21:48">]
Jeśli nie zostanie określony, kolejność będzie wykonywana w kolejności rosnącej. Możesz to określić, wykonując:
User.order(created_at: :asc)
#=> => [#<User id: 2, created_at: "2015-08-12 21:36:23">, #<User id: 11, created_at: "2015-08-15 10:21:48">]
User.order(created_at: :desc)
#=> [#<User id: 7585, created_at: "2016-07-13 17:15:27">, #<User id: 7583, created_at: "2016-07-13 16:51:18">]
.order
akceptuje również ciąg znaków, więc możesz to zrobić
User.order("created_at DESC")
#=> [#<User id: 7585, created_at: "2016-07-13 17:15:27">, #<User id: 7583, created_at: "2016-07-13 16:51:18">]
Ponieważ ciąg jest surowym SQL, możesz również określić tabelę, a nie tylko atrybut. Zakładając, że chcesz uporządkować users
według nazwy role
, możesz to zrobić:
Class User < ActiveRecord::Base
belongs_to :role
end
Class Role < ActiveRecord::Base
has_many :users
end
User.includes(:role).order("roles.name ASC")
Zakres order
może również akceptować węzeł Arel:
User.includes(:role).order(User.arel_table[:name].asc)
Metody ActiveRecord Bang (!)
Jeśli potrzebujesz metody ActiveRecord , aby zgłosić wyjątek zamiast false
wartości w przypadku niepowodzenia, możesz dodać !
do nich. To jest bardzo ważne. Ponieważ niektóre wyjątki / awarie są trudne do złapania, jeśli nie używasz! na nich. Zaleciłem zrobienie tego w cyklu programowania, aby w ten sposób napisać cały kod ActiveRecord, aby zaoszczędzić czas i kłopoty.
Class User < ActiveRecord::Base
validates :last_name, presence: true
end
User.create!(first_name: "John")
#=> ActiveRecord::RecordInvalid: Validation failed: Last name can't be blank
Metody ActiveRecord , które akceptują huk ( !
) To:
-
.create!
-
.take!
-
.first!
-
.last!
-
.find_by!
-
.find_or_create_by!
-
#save!
-
#update!
- wszystkie dynamiczne wyszukiwarki AR
.find_by
Możesz znaleźć rekordy według dowolnego pola w tabeli za pomocą find_by
.
Jeśli masz model User
z atrybutem first_name
, możesz:
User.find_by(first_name: "John")
#=> #<User id: 2005, first_name: "John", last_name: "Smith">
Pamiętaj, że find_by
nie find_by
wyjątku. Jeśli wynik jest pustym zestawem, zwraca nil
zamiast find
.
Jeśli potrzebny jest wyjątek, można użyć find_by!
, który wywołuje błąd ActiveRecord::RecordNotFound
taki jak find
.
.Usuń wszystko
Jeśli chcesz szybko usunąć wiele rekordów, ActiveRecord podaje metodę .delete_all
. do wywołania bezpośrednio w modelu, w celu usunięcia wszystkich rekordów w tej tabeli lub kolekcji. Uważaj jednak, ponieważ .delete_all
nie .delete_all
instancji żadnego obiektu, dlatego nie zapewnia żadnego wywołania zwrotnego ( before_*
i after_destroy
nie są wywoływane).
User.delete_all
#=> 39 <-- .delete_all return the number of rows deleted
User.where(name: "John").delete_all
Wyszukiwanie bez rozróżniania wielkości liter w ActiveRecord
Jeśli potrzebujesz wyszukać model ActiveRecord w celu znalezienia podobnych wartości, możesz ulec pokusie użycia LIKE
lub ILIKE
ale nie jest to przenośne między silnikami baz danych. Podobnie stosowanie zawsze zmniejszania lub zwiększania może powodować problemy z wydajnością.
Możesz użyć podstawowej metody Arel matches
ActiveRecord, aby zrobić to w bezpieczny sposób:
addresses = Address.arel_table
Address.where(addresses[:address].matches("%street%"))
Arel zastosuje odpowiednią konstrukcję LIKE lub ILIKE dla skonfigurowanego silnika bazy danych.
Zdobądź pierwszy i ostatni rekord
Szyny mają bardzo łatwy sposób na uzyskanie first
i last
rekordu z bazy danych.
Aby uzyskać first
rekord z tabeli users
, musimy wpisać następujące polecenie:
User.first
sql
następujące zapytanie sql
:
SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
I zwróci następujący rekord:
#<User:0x007f8a6db09920 id: 1, first_name: foo, created_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00, updated_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00 >
Aby uzyskać last
rekord z tabeli users
, musimy wpisać następujące polecenie:
User.last
sql
następujące zapytanie sql
:
SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
I zwróci następujący rekord:
#<User:0x007f8a6db09920 id: 10, first_name: bar, created_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00, updated_at: Thu, 16 Jun 2016 21:43:03 UTC +00:00 >
Przekazanie liczby całkowitej do pierwszej i ostatniej metody tworzy zapytanie LIMIT i zwraca tablicę obiektów.
User.first(5)
sql
następujące zapytanie sql
.
SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 5
I
User.last(5)
sql
następujące zapytanie sql
.
SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 5
.group i .count
Mamy model Product
i chcemy pogrupować je według category
.
Product.select(:category).group(:category)
Spowoduje to wysłanie zapytania do bazy danych w następujący sposób:
SELECT "product"."category" FROM "product" GROUP BY "product"."category"
Upewnij się, że pole zgrupowane jest również zaznaczone. Grupowanie jest szczególnie przydatne do zliczania występowania - w tym przypadku - categories
.
Product.select(:category).group(:category).count
Jak pokazuje zapytanie, użyje bazy danych do zliczania, co jest o wiele bardziej wydajne, niż pobieranie najpierw wszystkich rekordów i wykonywanie zliczania w kodzie:
SELECT COUNT("products"."category") AS count_categories, "products"."category" AS products_category FROM "products" GROUP BY "products"."category"
.distinct (lub .uniq)
Jeśli chcesz usunąć duplikaty z wyniku, możesz użyć .distinct()
:
Customers.select(:country).distinct
To wysyła zapytanie do bazy danych w następujący sposób:
SELECT DISTINCT "customers"."country" FROM "customers"
.uniq()
ma ten sam efekt. W przypadku Rails 5.0 stał się przestarzały i zostanie usunięty z Railsów w wersji 5.1. Powodem jest to, że słowo „ unique
nie ma takiego samego znaczenia jak wyraz „odrębny” i może wprowadzać w błąd. Ponadto distinct
jest bliższy składni SQL.
Łączy się
joins()
pozwala łączyć tabele z bieżącym modelem. Np.
User.joins(:posts)
wygeneruje następujące zapytanie SQL:
"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id""
Po dołączeniu do stołu będziesz mieć do niego dostęp:
User.joins(:posts).where(posts: { title: "Hello world" })
Zwróć uwagę na liczbę mnogą. Jeśli twoja relacja to :has_many
, to argument joins()
powinien być pluralizowany. W przeciwnym razie użyj liczby pojedynczej.
Zagnieżdżone joins
:
User.joins(posts: :images).where(images: { caption: 'First post' })
który wytworzy:
"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN "images" ON "images"."post_id" = "images"."id""
Obejmuje
ActiveRecord z includes
zapewnia, że wszystkie określone powiązania są ładowane przy użyciu minimalnej możliwej liczby zapytań. Tak więc podczas zapytania do tabeli o dane z powiązaną tabelą obie tabele są ładowane do pamięci.
@authors = Author.includes(:books).where(books: { bestseller: true } )
# this will print results without additional db hitting
@authors.each do |author|
author.books.each do |book|
puts book.title
end
end
Author.joins(:books).where(books: { bestseller: true } )
załaduje tylko autorów z warunkami do pamięci bez ładowania książek . Użyj joins
gdy dodatkowe informacje o zagnieżdżonych powiązaniach nie są wymagane.
@authors = Author.joins(:books).where(books: { bestseller: true } )
# this will print results without additional queries
@authors.each { |author| puts author.name }
# this will print results with additional db queries
@authors.each do |author|
author.books.each do |book|
puts book.title
end
end
Limit i przesunięcie
Możesz użyć limit
aby określić liczbę rekordów do pobrania, i offset
aby określić liczbę rekordów, które należy pominąć przed rozpoczęciem zwracania rekordów.
Na przykład
User.limit(3) #returns first three records
Wygeneruje następujące zapytanie SQL.
"SELECT `users`.* FROM `users` LIMIT 3"
Ponieważ przesunięcie nie jest wymienione w powyższym zapytaniu, zwróci pierwsze trzy rekordy.
User.limit(5).offset(30) #returns 5 records starting from 31th i.e from 31 to 35
Wygeneruje następujące zapytanie SQL.
"SELECT `users`.* FROM `users` LIMIT 5 OFFSET 30"