Buscar..


Introducción

ActiveRecord es la M en MVC, que es la capa del sistema responsable de representar los datos y la lógica de negocios. La técnica que conecta los objetos ricos de una aplicación a las tablas en un sistema de administración de base de datos relacional es O bject R elational M apper ( ORM ).

ActiveRecord realizará consultas en la base de datos por usted y es compatible con la mayoría de los sistemas de bases de datos. Independientemente del sistema de base de datos que esté utilizando, el formato del método ActiveRecord siempre será el mismo.

.dónde

El método where está disponible en cualquier modelo ActiveRecord y permite consultar la base de datos para un conjunto de registros que coincidan con los criterios dados.

El método where acepta un hash donde las claves corresponden a los nombres de columna en la tabla que representa el modelo.

Como ejemplo simple, usaremos el siguiente modelo:

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

Para encontrar a todas las personas con el nombre de Sven :

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

Para encontrar a todas las personas con el nombre de Sven y el apellido de Schrodinger :

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

En el ejemplo anterior, la salida sql muestra que sólo los registros serán devueltos si tanto el first_name y el last_name partido.

consulta con condición OR

Para buscar registros con el primer nombre first_name == 'Bruce' O el last_name == 'Wayne'

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

.where con una matriz

El método where en cualquier modelo ActiveRecord se puede usar para generar SQL de la forma WHERE column_name IN (a, b, c, ...) . Esto se logra pasando una matriz como argumento.

Como ejemplo simple, usaremos el siguiente modelo:

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

Si la matriz contiene un valor nil , el SQL se modificará para verificar si la columna es 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"

Alcances

Los ámbitos actúan como filtros predefinidos en los modelos ActiveRecord .

Un alcance se define utilizando el método de clase de scope .

Como ejemplo simple, usaremos el siguiente modelo:

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

Los ámbitos se pueden llamar directamente desde la clase modelo:

minors = Person.minors

Los alcances pueden ser encadenados:

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

El método where y otros métodos de tipo de consulta también se pueden encadenar:

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

Detrás de las escenas, los ámbitos son simplemente azúcar sintáctica para un método de clase estándar. Por ejemplo, estos métodos son funcionalmente idénticos:

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

# This ^ is the same as this:

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

Alcance predeterminado

en su modelo para establecer un alcance predeterminado para todas las operaciones en el modelo.

Hay una diferencia notable entre el scope método y un método de clase: scope -definida alcances siempre devolverá un ActiveRecord::Relation , incluso si la lógica dentro devuelve nil. Los métodos de clase, sin embargo, no tienen dicha red de seguridad y pueden romper la capacidad de cadena si devuelven algo más.

donde no

where cláusulas pueden ser negadas usando la sintaxis de 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')

Compatible con ActiveRecord 4.0 y versiones posteriores.

Ordenando

Puede ordenar los resultados de la consulta de ActiveRecord usando .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">]

Si no se especifica, el orden se realizará en orden ascendente. Puedes especificarlo haciendo:

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 también acepta una cadena, por lo que también podría hacer

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

Como la cadena es SQL sin formato, también puede especificar una tabla y no solo un atributo. Suponiendo que desea ordenar a los users según el nombre de su role , puede hacer esto:

Class User < ActiveRecord::Base
    belongs_to :role
end

Class Role < ActiveRecord::Base
  has_many :users
end

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

El alcance del order también puede aceptar un nodo Arel:

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

Métodos ActiveRecord Bang (!)

Si necesita un método ActiveRecord para generar una excepción en lugar de un valor false en caso de fallo, ¡puede agregar ! a ellos Esto es muy importante. ¡Como algunas excepciones / fallas son difíciles de detectar si no las usa! en ellos. Recomendé hacer esto en su ciclo de desarrollo para escribir todo su código ActiveRecord de esta manera para ahorrarle tiempo y problemas.

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

Los métodos de ActiveRecord que aceptan un bang ( ! ) Son:

  • .create!
  • .take!
  • .first!
  • .last!
  • .find_by!
  • .find_or_create_by!
  • #save!
  • #update!
  • todos los buscadores dinámicos AR

.find_by

Puede encontrar registros por cualquier campo en su tabla usando find_by .

Por lo tanto, si tiene un modelo de User con un atributo first_name , puede hacerlo:

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

find_by cuenta que find_by no lanza ninguna excepción de forma predeterminada. Si el resultado es un conjunto vacío, devuelve nil lugar de find .

Si la excepción es necesaria puede usar find_by! que plantea un error ActiveRecord::RecordNotFound como find .

.eliminar todos

Si necesita eliminar muchos registros rápidamente, ActiveRecord le da el método .delete_all . para ser llamado directamente en un modelo, para eliminar todos los registros en esa tabla, o una colección. Sin embargo, .delete_all cuidado, ya que .delete_all no .delete_all una instancia de ningún objeto, por lo tanto, no proporciona ninguna devolución de llamada ( before_* y after_destroy no se activan)

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

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

ActiveRecord caso de búsqueda insensible

Si necesita buscar valores similares en un modelo ActiveRecord, es posible que tenga la tentación de usar LIKE o ILIKE pero esto no es portátil entre los motores de base de datos. Del mismo modo, recurrir siempre a la reducción o mejora de la situación puede crear problemas de rendimiento.

Puede utilizar Arel subyacente de ActiveRecord matches método para hacer esto de una manera segura:

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

Arel aplicará la construcción LIKE o ILIKE apropiada para el motor de base de datos configurado.

Obtener primer y último registro

Los rieles tienen una forma muy sencilla de obtener el first y last registro de la base de datos.

Para obtener el first registro de la tabla de users , debemos escribir el siguiente comando:

User.first

Se generará la siguiente consulta sql :

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

Y volverá el siguiente registro:

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

Para obtener el last registro de la tabla de users , debemos escribir el siguiente comando:

User.last

Se generará la siguiente consulta sql :

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

Y volverá el siguiente registro:

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

Pasar un entero al primer y último método crea una consulta LIMIT y devuelve una matriz de objetos.

User.first(5)

Se generará la siguiente consulta sql .

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

Y

User.last(5)

Se generará la siguiente consulta sql .

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

.group y .count

Tenemos un modelo de Product y queremos agruparlos por category .

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

Esto consultará la base de datos de la siguiente manera:

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

Asegúrese de que el campo agrupado también esté seleccionado. La agrupación es especialmente útil para contar la ocurrencia, en este caso, de las categories .

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

Como muestra la consulta, utilizará la base de datos para el conteo, que es mucho más eficiente, que recuperar todo el registro primero y hacer el conteo en el código:

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

.distinto (o .uniq)

Si desea eliminar duplicados de un resultado, puede usar .distinct() :

Customers.select(:country).distinct

Esto consulta la base de datos de la siguiente manera:

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

.uniq() tiene el mismo efecto. Con Rails 5.0 quedó en desuso y se eliminará de Rails con la versión 5.1. La razón es que la palabra unique no tiene el mismo significado que distinta y puede ser engañosa. Además distinct está más cerca de la sintaxis de SQL.

Se une

joins() permite unir tablas a su modelo actual. Por ej.

User.joins(:posts)

producirá la siguiente consulta SQL:

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

Teniendo mesa unida, tendrás acceso a ella:

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

Preste atención en forma plural. Si su relación es :has_many , entonces el argumento joins() debe estar pluralizado. De lo contrario, utilice singular.

Anidada joins :

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

que producirá:

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

Incluye

ActiveRecord con includes garantiza que todas las asociaciones especificadas se carguen utilizando el número mínimo posible de consultas. Por lo tanto, al consultar datos de una tabla con una tabla asociada, ambas tablas se cargan en la memoria.

@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 } ) cargará solo a los autores con condiciones en la memoria sin cargar libros . Utilice joins cuando no se requiera información adicional sobre asociaciones anidadas.

@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

Límite y compensación

Puede usar el limit para indicar el número de registros que se van a recuperar, y usar la offset para indicar el número de registros que se deben omitir antes de comenzar a devolver los registros.

Por ejemplo

User.limit(3) #returns first three records

Se generará la siguiente consulta sql.

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

Como el desplazamiento no se menciona en la consulta anterior, devolverá los primeros tres registros.

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

Se generará la siguiente consulta sql.

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


Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow