Sök…


Introduktion

ActiveRecord är M i MVC som är det lager i systemet som ansvarar för att representera affärsdata och logik. Tekniken som kopplar en applikations rika objekt till tabeller i ett relationsdatabashanteringssystem är O bject R elational M apper ( ORM ).

ActiveRecord kommer att utföra frågor i databasen för dig och är kompatibel med de flesta databassystem. Oavsett vilket databassystem du använder kommer ActiveRecord-metodformatet alltid att vara detsamma.

.var

where metoden finns tillgänglig på alla ActiveRecord modeller och gör det möjligt att fråga databasen efter en uppsättning poster som matchar de givna kriterierna.

where metoden accepterar en hash där tangenterna motsvarar kolumnnamnen i tabellen som modellen representerar.

Som ett enkelt exempel kommer vi att använda följande modell:

class Person < ActiveRecord::Base
  #attribute :first_name, :string
  #attribute :last_name, :string
end

För att hitta alla personer med förnamnet Sven :

people = Person.where(first_name: 'Sven')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven'"

För att hitta alla personer med förnamnet Sven och efternamnet på Schrodinger :

people = Person.where(first_name: 'Sven', last_name: 'Schrodinger')
people.to_sql # "SELECT * FROM people WHERE first_name='Sven' AND last_name='Schrodinger'"

I exemplet ovan visar sql-utdata att poster endast kommer att returneras om både first_name och last_name matchar.

fråga med ELLER villkor

Att hitta poster med first_name == 'Bruce' ELLER last_name == 'Wayne'

User.where('first_name = ? or last_name = ?', 'Bruce', 'Wayne')
# SELECT "users".* FROM "users" WHERE (first_name = 'Bruce' or last_name = 'Wayne')

.var med en matris

where metoden på vilken ActiveRecord-modell som helst kan användas för att generera SQL med formen WHERE column_name IN (a, b, c, ...) . Detta uppnås genom att skicka en grupp som argument.

Som ett enkelt exempel kommer vi att använda följande modell:

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')"

Om matrisen innehåller en nil kommer SQL att ändras för att kontrollera om kolumnen är 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 fungerar som fördefinierade filter på ActiveRecord modeller.

Ett omfattningsområde definieras med metod för scope .

Som ett enkelt exempel kommer vi att använda följande modell:

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

Omfång kan kallas direkt från modellklassen:

minors = Person.minors

Räckvidd kan kedjas:

peters_children = Person.minors.with_last_name('Peters')

where metoden och andra frågestypmetoder kan också kedjas:

mary_smith = Person.with_last_name('Smith').where(first_name: 'Mary')

Bakom kulisserna är scopes helt enkelt syntaktiskt socker för en standardklassmetod. Till exempel är dessa metoder funktionellt identiska:

scope :with_last_name, ->(name) { where(name: name) }

# This ^ is the same as this:

def self.with_last_name(name)
  where(name: name)
end

Standardområde

i din modell för att ange ett standardomfång för alla operationer på modellen.

Det finns en anmärkningsvärd skillnad mellan scope och en klassmetod: scope kommer alltid att returnera ett ActiveRecord::Relation , även om logiken inom returnerar noll. Klassmetoder har emellertid inget sådant säkerhetsnät och kan bryta kedjan om de returnerar något annat.

where.not

where klausuler kan negeras med hjälp av 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')

Stöds av ActiveRecord 4.0 och senare.

Beställning

Du kan beställa ActiveRecord- frågeställningar med .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">]

Om det inte anges kommer beställningen att utföras i stigande ordning. Du kan ange det genom att göra:

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 accepterar också en sträng, så du kan också göra det

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">]

Eftersom strängen är rå SQL kan du också ange en tabell och inte bara ett attribut. Förutsatt att du vill beställa users enligt deras role kan du göra detta:

Class User < ActiveRecord::Base
    belongs_to :role
end

Class Role < ActiveRecord::Base
  has_many :users
end

User.includes(:role).order("roles.name ASC")

order kan också acceptera en Arel-nod:

User.includes(:role).order(User.arel_table[:name].asc)

ActiveRecord Bang (!) -Metoder

Om du behöver en ActiveRecord- metod för att höja ett undantag istället för ett false värde vid fel kan du lägga till ! till dem. Det här är väldigt viktigt. Som vissa undantag / misslyckanden är svåra att fånga om du inte använder det! på dem. Jag rekommenderade att du gör detta i din utvecklingscykel för att skriva all din ActiveRecord-kod på detta sätt för att spara tid och problem.

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

ActiveRecord- metoderna som accepterar ett slag ( ! ) Är :

  • .create!
  • .take!
  • .first!
  • .last!
  • .find_by!
  • .find_or_create_by!
  • #save!
  • #update!
  • alla dynamiska AR-sökare

.find_by

Du kan hitta poster efter valfritt fält i tabellen med find_by .

Så om du har en User modell med first_name attribut du kan göra:

User.find_by(first_name: "John")
#=> #<User id: 2005, first_name: "John", last_name: "Smith">

Tänk på att find_by inte kastar något undantag som standard. Om resultatet är en tom uppsättning, returnerar det nil istället för att find .

Om undantaget behövs kan du find_by! som höjer ett ActiveRecord::RecordNotFound fel som find .

.radera allt

Om du behöver ta bort en hel del skivor snabbt ger Active .delete_all metod. att kallas direkt på en modell, för att radera alla poster i den tabellen eller en samling. Akta dig dock, eftersom .delete_all inte ger något objekt och därför inte ger någon återuppringning ( before_* och after_destroy inte after_destroy ).

User.delete_all
#=> 39  <-- .delete_all return the number of rows deleted

User.where(name: "John").delete_all 

ActiveRecord-okänslig sökning

Om du behöver söka i en ActiveRecord-modell efter liknande värden kanske du frestas att använda LIKE eller ILIKE men detta är inte portabelt mellan databasmotorer. På liknande sätt kan man skapa prestandafrågor att ta tillvara alltid att stänga ned eller stiga.

Du kan använda ActiveRecords underliggande Arel- matches för att göra detta på ett säkert sätt:

addresses = Address.arel_table
Address.where(addresses[:address].matches("%street%"))

Arel kommer att använda lämpliga LIKE- eller ILIKE-konstruktioner för den konfigurerade databasmotorn.

Få första och sista posten

Rails har mycket enkelt sätt att få first och last posten från databasen.

För att få den first posten från users måste vi skriva följande kommando:

User.first

Det genererar följande sql fråga:

SELECT  `users`.* FROM `users`  ORDER BY `users`.`id` ASC LIMIT 1

Och kommer tillbaka efter posten:

#<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 >

För att få den last posten från users måste vi skriva följande kommando:

User.last

Det genererar följande sql fråga:

SELECT  `users`.* FROM `users`  ORDER BY `users`.`id` DESC LIMIT 1

Och kommer tillbaka efter posten:

#<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 >

Att skicka ett heltal till den första och sista metoden skapar en LIMIT- fråga och returnerar en rad objekt.

User.first(5)

Det kommer att generera följande sql fråga.

SELECT  "users".* FROM "users"  ORDER BY "users"."id" ASC LIMIT 5

Och

User.last(5)

Det kommer att generera följande sql fråga.

SELECT  "users".* FROM "users"  ORDER BY "users"."id" DESC LIMIT 5

.grupp och .antal

Vi har en Product och vi vill gruppera dem efter deras category .

Product.select(:category).group(:category)

Detta frågar databasen på följande sätt:

SELECT "product"."category" FROM "product" GROUP BY "product"."category"

Se till att det grupperade fältet också är valt. Gruppering är särskilt användbar för att räkna förekomsten - i det här fallet - av categories .

Product.select(:category).group(:category).count

Som frågan visar kommer den att använda databasen för att räkna, vilket är mycket effektivare än att hämta all post först och göra räkningen i koden:

SELECT COUNT("products"."category") AS count_categories, "products"."category" AS products_category FROM "products" GROUP BY "products"."category"

.distinkt (eller .uniq)

Om du vill ta bort dubbletter från ett resultat kan du använda .distinct() :

Customers.select(:country).distinct

Detta frågar databasen på följande sätt:

SELECT DISTINCT "customers"."country" FROM "customers"

.uniq() har samma effekt. Med Rails 5.0 försvann den och den kommer att tas bort från Rails med version 5.1. Anledningen är att ordet unique inte har samma betydelse som distinkt och det kan vara vilseledande. Dessutom distinct är närmare SQL-syntaxen.

Fogar

joins() låter dig koppla tabeller till din nuvarande modell. För ex.

User.joins(:posts)

kommer att producera följande SQL-fråga:

"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id""

När du har anslutit tabellen har du tillgång till det:

User.joins(:posts).where(posts: { title: "Hello world" })

Var uppmärksam på plural form. Om din relation är :has_many , bör argumentet för joins() vara pluraliserat. Använd annars singular.

Nested joins :

User.joins(posts: :images).where(images: { caption: 'First post' })

som kommer att producera:

"SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" INNER JOIN "images" ON "images"."post_id" = "images"."id""

inkluderar

ActiveRecord med includes säkerställer att alla angivna föreningar laddas med minsta möjliga antal frågor. Så när du frågar en tabell för data med en tillhörande tabell, laddas båda tabellerna i minnet.

@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 } ) laddar bara författare med villkor i minnet utan att ladda böcker . Använd joins när ytterligare information om kapslade föreningar inte krävs.

@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

Begränsning och förskjutning

Du kan använda limit att berätta antalet poster som ska hämtas och använda offset att berätta antalet poster som ska hoppas över innan du börjar returnera posterna.

Till exempel

User.limit(3) #returns first three records

Det kommer att generera följande sql-fråga.

"SELECT  `users`.* FROM `users` LIMIT 3"

Eftersom offset inte nämns i ovanstående fråga så kommer det att returnera de tre första posterna.

User.limit(5).offset(30) #returns 5 records starting from 31th i.e from 31 to 35

Det kommer att generera följande sql-fråga.

"SELECT  `users`.* FROM `users` LIMIT 5 OFFSET 30"


Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow