Ruby on Rails
ActiveRecord-queryinterface
Zoeken…
Invoering
ActiveRecord is de M in MVC, de laag van het systeem die verantwoordelijk is voor het weergeven van bedrijfsgegevens en logica. De techniek die de rijke objecten van een toepassing verbindt met tabellen in een relationeel databasebeheersysteem is O bject R elational M apper ( ORM ).
ActiveRecord voert voor u zoekopdrachten uit in de database en is compatibel met de meeste databasesystemen. Ongeacht het databasesysteem dat u gebruikt, de indeling van de ActiveRecord-methode is altijd hetzelfde.
.waar
De where
methode is beschikbaar op een ActiveRecord
model en maakt het mogelijk het opvragen van de database voor een set records die overeenkomen met de opgegeven criteria.
De where
Werkwijze accepteert een hash waarbij de toetsen komen overeen met de kolom namen op de tafel dat het model vertegenwoordigt.
Als een eenvoudig voorbeeld gebruiken we het volgende model:
class Person < ActiveRecord::Base
#attribute :first_name, :string
#attribute :last_name, :string
end
Om alle mensen met de voornaam Sven
:
people = Person.where(first_name: 'Sven')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven'"
Om alle mensen met de voornaam Sven
en achternaam van Schrodinger
:
people = Person.where(first_name: 'Sven', last_name: 'Schrodinger')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven' AND last_name='Schrodinger'"
In het bovenstaande voorbeeld toont de sql-uitvoer dat records alleen worden geretourneerd als zowel de first_name
als de last_name
overeenkomen.
zoekopdracht met OF-voorwaarde
Records zoeken met first_name == 'Bruce'
OF last_name == 'Wayne'
User.where('first_name = ? or last_name = ?', 'Bruce', 'Wayne')
# SELECT "users".* FROM "users" WHERE (first_name = 'Bruce' or last_name = 'Wayne')
.waar met een array
De where
Werkwijze Elk ActiveRecord model kan worden gebruikt om de vorm van SQL genereren WHERE column_name IN (a, b, c, ...)
. Dit wordt bereikt door een array als argument door te geven.
Als een eenvoudig voorbeeld gebruiken we het volgende model:
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')"
Als de array een nil
bevat, wordt de SQL gewijzigd om te controleren of de kolom 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"
scopes
Scopes fungeren als vooraf gedefinieerde filters op ActiveRecord
modellen.
Een scope wordt gedefinieerd met behulp van de scope
class-methode.
Als een eenvoudig voorbeeld gebruiken we het volgende model:
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
Scopes kunnen direct uit de modelklasse worden opgeroepen:
minors = Person.minors
Scopes kunnen worden geketend:
peters_children = Person.minors.with_last_name('Peters')
De where
Werkwijze en andere type query werkwijzen kunnen ook worden gekoppeld:
mary_smith = Person.with_last_name('Smith').where(first_name: 'Mary')
Achter de schermen zijn scopen gewoon syntactische suiker voor een standaardklasse methode. Deze methoden zijn bijvoorbeeld functioneel identiek:
scope :with_last_name, ->(name) { where(name: name) }
# This ^ is the same as this:
def self.with_last_name(name)
where(name: name)
end
Standaard bereik
in uw model om een standaardbereik in te stellen voor alle bewerkingen op het model.
Er is een opmerkelijk verschil tussen de
scope
methode en een class-methode:scope
gedefinieerde scopes retourneren altijd eenActiveRecord::Relation
, zelfs als de logica binnen nul teruggeeft. Klasse-methoden hebben echter niet zo'n vangnet en kunnen de kettingbaarheid verbreken als ze iets anders teruggeven.
waar niet
where
clausules kunnen worden ontkend met behulp van de where.not
syntaxis:
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')
Ondersteund door ActiveRecord 4.0 en hoger.
Bestellen
U kunt ActiveRecord- queryresultaten bestellen met .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">]
Indien niet gespecificeerd, wordt de bestelling in oplopende volgorde uitgevoerd. U kunt het opgeven door het volgende te doen:
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
accepteert ook een string, dus dat kan ook
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">]
Omdat de tekenreeks onbewerkte SQL is, kunt u ook een tabel opgeven en niet alleen een kenmerk. Ervan uitgaande dat u wilt bestellen users
op basis van hun role
naam, kunt u dit doen:
Class User < ActiveRecord::Base
belongs_to :role
end
Class Role < ActiveRecord::Base
has_many :users
end
User.includes(:role).order("roles.name ASC")
De order
scope kan aanvaarden Arel knooppunt:
User.includes(:role).order(User.arel_table[:name].asc)
ActiveRecord Bang (!) -Methoden
Als u een ActiveRecord- methode nodig hebt om een uitzondering te genereren in plaats van een false
waarde in geval van een storing, kunt u toevoegen !
naar hen. Dit is erg belangrijk. Omdat sommige uitzonderingen / storingen moeilijk te vangen zijn als u deze niet gebruikt! op hen. Ik raad aan dit in je ontwikkelingscyclus te doen om al je ActiveRecord-code op deze manier te schrijven om je tijd en moeite te besparen.
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
De ActiveRecord- methoden die een knal accepteren ( !
) Zijn:
-
.create!
-
.take!
-
.first!
-
.last!
-
.find_by!
-
.find_or_create_by!
-
#save!
-
#update!
- alle AR dynamische vinders
.find_by
U kunt records zoeken op elk veld in uw tabel met behulp van find_by
.
Dus als u een User
met het kenmerk first_name
kunt u het volgende doen:
User.find_by(first_name: "John")
#=> #<User id: 2005, first_name: "John", last_name: "Smith">
Let wel dat find_by
standaard geen uitzondering find_by
. Als het resultaat een lege set is, retourneert deze nil
plaats van te find
.
Als de uitzondering nodig is, kunt u find_by!
dat een ActiveRecord::RecordNotFound
fout veroorzaakt, zoals find
.
.Verwijder alles
Als u veel platen snel wilt verwijderen, ActiveRecord geeft .delete_all
methode. om rechtstreeks op een model te worden opgeroepen, om alle records in die tabel of een verzameling te verwijderen. Maar let op, aangezien .delete_all
geen object instantieert en dus geen callback biedt ( before_*
en after_destroy
worden niet geactiveerd).
User.delete_all
#=> 39 <-- .delete_all return the number of rows deleted
User.where(name: "John").delete_all
ActiveRecord hoofdletterongevoelig zoeken
Als u in een ActiveRecord-model naar vergelijkbare waarden moet zoeken, kunt u in de verleiding komen om LIKE
of ILIKE
maar dit is niet draagbaar tussen database-engines. Evenzo kan het gebruik van altijd downcasing of upcasing prestatieproblemen veroorzaken.
U kunt de onderliggende methode van Arel- matches
ActiveRecord gebruiken om dit op een veilige manier te doen:
addresses = Address.arel_table
Address.where(addresses[:address].matches("%street%"))
Arel past het juiste LIKE- of ILIKE-construct toe voor de geconfigureerde database-engine.
Ontvang eerste en laatste record
Rails hebben een zeer eenvoudige manier om het first
en last
record uit de database te krijgen.
Om het first
record van de users
te krijgen, moeten we het volgende commando typen:
User.first
Het zal de volgende sql
query genereren:
SELECT `users`.* FROM `users` ORDER BY `users`.`id` ASC LIMIT 1
En zal volgend record terugkeren:
#<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 >
Om het last
record van de users
te krijgen, moeten we het volgende commando typen:
User.last
Het zal de volgende sql
query genereren:
SELECT `users`.* FROM `users` ORDER BY `users`.`id` DESC LIMIT 1
En zal volgend record terugkeren:
#<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 >
Als u een geheel getal doorgeeft aan de eerste en laatste methode, wordt een LIMIT- query gemaakt en wordt een array met objecten geretourneerd.
User.first(5)
Het zal de volgende sql
query genereren.
SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 5
En
User.last(5)
Het zal de volgende sql
query genereren.
SELECT "users".* FROM "users" ORDER BY "users"."id" DESC LIMIT 5
.groep en .count
We hebben een Product
en we willen ze groeperen op category
.
Product.select(:category).group(:category)
Dit zal de database als volgt doorzoeken:
SELECT "product"."category" FROM "product" GROUP BY "product"."category"
Zorg ervoor dat het gegroepeerde veld ook is geselecteerd. Groeperen is met name handig voor het tellen van het voorkomen - in dit geval - van categories
.
Product.select(:category).group(:category).count
Zoals de query laat zien, zal het de database gebruiken om te tellen, wat veel efficiënter is, dan eerst alle records op te halen en de code te tellen:
SELECT COUNT("products"."category") AS count_categories, "products"."category" AS products_category FROM "products" GROUP BY "products"."category"
.distinct (of .uniq)
Als u duplicaten uit een resultaat wilt verwijderen, kunt u .distinct()
:
Customers.select(:country).distinct
Hiermee wordt de database als volgt opgevraagd:
SELECT DISTINCT "customers"."country" FROM "customers"
.uniq()
heeft hetzelfde effect. Met Rails 5.0 werd het verouderd en wordt het met versie 5.1 van Rails verwijderd. De reden is dat het woord unique
niet dezelfde betekenis heeft als verschillend en het kan misleidend zijn. Verder distinct
is dichter bij de SQL-syntaxis.
Doet mee
joins()
kunt u tabellen koppelen aan uw huidige model. Voor ex.
User.joins(:posts)
zal de volgende SQL-query produceren:
"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id""
Als u aan tafel zit, heeft u toegang tot:
User.joins(:posts).where(posts: { title: "Hello world" })
Let op meervoudsvorm. Als uw relatie is :has_many
, dan moet het argument joins()
meervoudig zijn. Gebruik anders enkelvoud.
Geneste joins
:
User.joins(posts: :images).where(images: { caption: 'First post' })
die zal produceren:
"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN "images" ON "images"."post_id" = "images"."id""
Inclusief
ActiveRecord met includes
zorgt ervoor dat alle opgegeven koppelingen worden geladen met behulp van het minimaal mogelijke aantal query's. Dus wanneer u een tabel opvraagt naar gegevens met een bijbehorende tabel, worden beide tabellen in het geheugen geladen.
@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 } )
laadt alleen auteurs met voorwaarden in het geheugen zonder boeken te laden . Gebruik joins
wanneer aanvullende informatie over geneste koppelingen niet vereist is.
@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
Limiet en offset
U kunt limit
gebruiken om het aantal op te halen records te vertellen, en offset
gebruiken om het aantal records te vertellen dat moet worden overgeslagen voordat u begint met het retourneren van de records.
Bijvoorbeeld
User.limit(3) #returns first three records
Het zal de volgende sql-query genereren.
"SELECT `users`.* FROM `users` LIMIT 3"
Omdat offset niet wordt vermeld in de bovenstaande query, retourneert het de eerste drie records.
User.limit(5).offset(30) #returns 5 records starting from 31th i.e from 31 to 35
Het zal de volgende sql-query genereren.
"SELECT `users`.* FROM `users` LIMIT 5 OFFSET 30"