08. ORM & Active Records CRUD Methods - williamromero/curso-rails GitHub Wiki

FINDER METHODS - ACTIVE RECORD

OBTENER UN OBJETO - ActiveRecord::FinderMethods

  # Obtener primer registro
  Product.first
  # Product Load (0.2ms)  SELECT `products`.* FROM `products` ORDER BY `products`.`id` ASC LIMIT 1
  # => #<Product id: 1, name: "Pepsi Cola", description: "Bebida de soda", stock: 1, price: 0.55e1, created_at: "2022-05-30 13:59:13.599556000 +0000", updated_at: "2022-06-02 16:53:34.419952000 +0000">

  # Obtener último registro
  Product.last
  # Product Load (0.3ms)  SELECT `products`.* FROM `products` ORDER BY `products`.`id` DESC LIMIT 1
  # => #<Product id: 16, name: "Galletas Gama", description: "Galletas saladas", stock: 5, price: 0.0, created_at: "2022-06-02 05:51:21.009150000 +0000", updated_at: "2022-06-02 05:51:21.009150000 +0000"> 

  # Obtiene todos los registros
  Product.all

  # El método find obtiene el primer registro que posea el id, número 41.
  product = Product.find 41 # => 1 record
  # => #<Product id: 41, code: "P220603270", name: "Ergonomic Steel Table", uuid: "1d2772f9-c66d-4816-82b3-2a7017ade538", description: "Maiores in sed cumque.", stock: 15, price: 0.2813e4, created_at: ..., updated_at: ... >

OBTENER MÚLTIPLES OBJECTOS

Métodos FIND & FIND_BY - ActiveRecord::FinderMethods

Buscando registros por llaves primarias.

  # USOS DEL FIND
  
  # Obtener una lista de productos por atributo id
  product = Product.find 31, 45, 51
  product.size # => 3 products on array.
  # => [#<Product id: 31, code: "P220603550", name: "Awesome Iron Knife", uuid: "6fcfa595-ae8b-46d6-876d-7c3692e3daf9", description: "Sunt sed eius minima.", stock: 20, price: 0.3889e4, created_at: "2022-06-03 06:56:29.742630000 +0000", updated_at: "2022-06-03 06:56:29.742630000 +0000">, #<Product id: 45, code: "P220603957", name: "Gorgeous Paper Plate", uuid: "1b0ffb97-8a0c-481b-98a6-e9ed146f1723", description: "Aut officiis quo magni.", stock: 23, price: 0.1467e4, created_at: "2022-06-03 06:56:29.816677000 +0000", updated_at: "2022-06-03 06:56:29.816677000 +0000">, #<Product id: 51, code: "P220603522", name: "Lightweight Cotton Pants", uuid: "ecb9fe3d-9cd9-4eb8-a373-468ba4a2eba9", description: "Et repellendus unde autem.", stock: 9, price: 0.1826e4, created_at: "2022-06-03 07:05:22.350961000 +0000", updated_at: "2022-06-03 07:05:22.350961000 +0000">]
  
  # get error when id not exists
  product = Product.find 31, 43, 58 #=> throws Exception: `raise_record_not_found_exception!': Couldn't find all Products with 'id': (31, 43, 58) (found 2 results, but was looking for 3). (ActiveRecord::RecordNotFound) 


  # find product by attribute
  product = Product.find_by code: 'P43211' # => 1 record
  # => 1 record <Product:0x00007fadb95bfd60 id: 1, title: "Producto 1", code: "P001", stock: 10, price: 100000, uuid: "a0eb2f7f-55d9-4c00-a761-d43af856697e" >

Método WHERE - ActiveRecord::QueryMethods

  # En consola
  Product.where(price: 100000..400000) # Obtiene todos los productos que tengan un precio entre 100000 y 400000
  
  # Obtiene todos los productos los cuales su stock sea igual a 20.
  Product.where(stock: 20)
  # => [#<Product:0x00007fc28087e9d0 id: 2, title: "Producto 2", stock: 20, price: 200000, created_at: Fri, 08 Apr 2022 02:19:24.250355000 UTC +00:00, updated_at: Fri, 08 Apr 2022 02:19:24.250355000 UTC +00:00>]

  # Obtiene los productos que su precio esté entre 100000 y 400000 y que su stock sea menor que 20
  Product.where(price: 100000..400000).where('stock < ?', 20)

  # Obtiene los registros que estén entre el rango de precio, que tengan un stock de 20
  # pero lo más importante, nos devuelve el número de registros.
  Product.where(price: 100000..400000, stock: 20).count # => 1 record

  # Obtiene los registros con un precio menor o igual a 200000 y que posean un stock menor a 30
  Product.where('price <= ? and stock < ?', 200000, 30) # => 2 records

  # El método OR nos ayuda a concatenar nuevas relaciones usando únicamente el método WHERE.
  Product.where(price: 200000..400000).or(Product.where('title LIKE ?', "%Prod")).or(Product.where('stock < ?', 40))
  # => 4 records

Por Batches - ActiveRecord::QueryMethods

Esta opción, se utiliza normalmente cuando se debe de iterar sobre un gran monto de registros. Por ejemplo, a la hora de enviar un correo a una base de usuarios, será más fácil manipular mil registros por cada X segundos, que una tabla completa de millones de registros que luego deban de realizar una acción. A continuación un ejemplo:

  User.each { |user| NewsLetter.weekly_deliver(user) }

Para esto, se utilizan diferentes métodos pero quizás el más común es find_in_batches. A continuación, tomaré un ejemplo de nuestra base de datos la cual hemos crecido hasta 200 registros.

  Product.select([:id, :name, :price]).find_in_batches(batch_size: 100, start:0).each do |prd|
    p prd.select {|p| p.price < 500 }.size 
  end
  # Product Load (0.4ms)  SELECT `products`.`id`, `products`.`name`, `products`.`price` FROM `products` WHERE `products`.`id` >= 0 ORDER BY `products`.`id` ASC LIMIT 100 
  # => 12

  # Product Load (0.6ms)  SELECT `products`.`id`, `products`.`name`, `products`.`price` FROM `products` WHERE `products`.`id` >= 0 AND `products`.`id` > 100 ORDER BY `products`.`id` ASC LIMIT 100
  # => 6

Si nos fijamos en las dos peticiones que hará a la base de datos, estas irán de 0 a 100 y luego de 100 hacia dela y así cada 100 registros. La guía de referencia siempre será el ID, por lo que si hay registros eliminados previamente, los resultados pueden variar. A continuación, otro ejemplo de uso del find_in_batches:

  Product.find_in_batches(batch_size: 100, start:0).each { |prd| p prd.select {|p| p.stock < 2 }.size }

  # Product Load (0.5ms)  SELECT `products`.* FROM `products` WHERE `products`.`id` >= 0 ORDER BY `products`.`id` ASC LIMIT 100
  # => 3
  # Product Load (0.6ms)  SELECT `products`.* FROM `products` WHERE `products`.`id` >= 0 AND `products`.`id` > 100 ORDER BY `products`.`id` ASC LIMIT 100
  # => 2


  # Otro ejemplo:
  Product.find_in_batches(batch_size: 100, start: 0).each { |prd| p prd.select { |record| record.price < 200 }.size }
  # SQL Query => 5
  # SQL Query => 9
  # SQL Query => 3
  # Result of records => 17

  filtered_products = []
  Product.find_in_batches(batch_size: 100, start: 0).each { |prd| prd.select { |record| filtered_products << record unless record.price > 200 } }
  filtered_products.size # => 17

CONTAR LOS OBJETOS DE UNA BÚSQUEDA

Una tarea que es necesaria dentro de los requerimientos que comúnmente se hacen a la base de datos es la de contabilizar en número de registros que contiene una consulta. En ROR, existen tres métodos para realizar dicha tarea que veremos a continuación. Estos funcionan de diferentes formas, pero responden a un funcionamiento común:

TODAS LAS PETICIONES EN RUBY ON RAILS, SON CARGADAS EN MEMORIA QUE SE EJECUTAN HASTA QUE LA INFORMACIÓN VA A UTILIZARSE EN EL CÓDIGO, POR LO QUE EL EMPLEO DEL MÉTODO LOAD ES NECESARIO PARA OBTENER LOS REGISTROS DE LA BASE DE DATOS SI NO SE HAN SOLICITADO ANTES PARA OTRA TAREA. ES DECIR,

METODO COUNT

Cuando el método es llamado en un objeto ActiveRecord Relation, el método count ejecutará una consulta SQL COUNT.

  products = Product.count
  #  (0.6ms)  SELECT COUNT(*) FROM `products`
  # => 500

METODO LENGTH

length es un método plano de Ruby que sirve para contar cómo el número de elementos, como por ejemplo, de un array. La diferencia es que al no existir un método length* nativo del ActiveRecord, cuando hacemos el llamado del método length con un objeto *ActiveRecord Relation, este lo convierte en un array y cuenta el número de objetos. Esta tarea, es obviamente más lenta que el count.

Un aspecto importante a tomar en cuenta es que si la lista de objetos aún no está loaded?, el método ejecutará la operación load para poder obtener los registros y contarlos.

  products = Product.all
  # Product Load (0.4ms)  SELECT `products`.* FROM `products` /* loading for inspect */ LIMIT 11
  # => #<ActiveRecord::Relation [#<Product id: 307, code: "P220608958", name: "Gorgeous Silk Shoes", uuid: "9f7117b1-21e0-4fce-91de-db526b2da2c3", description: "Et ... 
  
  products.length # => 500
  # Product Load (1.0ms)  SELECT `products`.* FROM `products`
   
  products.length
  # => 500

MÉTODO SIZE

Como el método count, size funciona diferente dependiendo de si lo llama en un array o en un objeto ActiveRecord Relation. Cuando es llamado por un array, size funciona igual que el método length: retorna el número de objetos en el array. Pero las cosas se vuelven interesantes cuando se llama al método desde una ActiveRecord::Relation. En ese caso, su comportamiento es diferente dependiendo de si la relación de registros ha sido cargada de la base de datos o no. Si los registros ya han sido cargados, entonces podremos llamar el método size para obtener el tamaño de la asociación (ActiveRecord::Relation), sino, este hará una query a la base de datos para obtener el valor.

  products = Product.all
  # Product Load (0.3ms)  SELECT `products`.* FROM `products` /* loading for inspect */ LIMIT 11

  products.load
  # Product Load (0.9ms)  SELECT `products`.* FROM `products`
  # => <ActiveRecord::Relation [#<Product id: 307, code: "P220608958", name: "Gorgeous Silk Shoes", uuid: "9f7117b1-21e0....
  
  products.size
  # => 500

  # SI EL PRODUCTO NO ESTÁ CARGADO
  products = Product.all
  # Product Load (0.3ms)  SELECT `products`.* FROM `products` /* loading for inspect */ LIMIT 11

  # El metodo SIZE ejecutará la query a la base de datos con el método count
  # (0.8ms)  SELECT COUNT(*) FROM `products`
  # => 500

SELECCIONAR COLUMNAS ESPECÍFICAS

SELECT

El método SELECT nos permite elegir las columnas que necesitamos evitando obtener todos los campos de un grupo de registros.

  products = Product.order(price: :desc).limit(3).select :id, :title, :price
  # Product Load (0.5ms)  SELECT `products`.`id`, `products`.`title`, `products`.`price` FROM `products` ORDER BY `products`.`price` DESC LIMIT 3 

  # [<Product:0x00007fe325e8aac0 id: 43, title: "Lightweight Paper Table", price: 9953368>,
  # <Product:0x00007fe325e8a9f8 id: 8, title: "Synergistic Leather Shoes", price: 9944133>,
  # <Product:0x00007fe325e8a930 id: 48, title: "Practical Rubber Bench", price: 9914656> ]

El método select, siempre regresa una lista de objetos.

PLUCK

El método PLUCK funciona de forma similar que el método SELECT pero la diferencia principal entre ambos métodos es la forma en que retorna la información que es requerida. El método pluck retorna una lista de valores depositados entre corchetes []. Cuando se requiere más de una columna, esto retorna una lista de listas.

  products = Product.order(price: :desc).limit(3).pluck :id, :title, :price
  # Product Pluck (0.4ms)  SELECT `products`.`id`, `products`.`title`, `products`.`price` FROM `products` ORDER BY `products`.`price` DESC LIMIT 3
  # => [[43, "Lightweight Paper Table", 9953368], [8, "Synergistic Leather Shoes", 9944133], [48, "Practical Rubber Bench", 9914656]]

BUSCAR O CREAR

FIND_OR_CREATE_BY

El método find_or_create_by busca el primer registro con los atributos dados o crea un registro con los atributos dados si no lo ha encontrado:

  product = Product.find_or_create_by title: 'Green Tshirt', stock: 40, price: '900000', code: Random.rand(40000..90000)
  
  # FINDING PRODUCT
  # Product Load (0.3ms)  SELECT `products`.* FROM `products` WHERE `products`.`title` = 'Green Tshirt' AND `products`.`stock` = 40 AND `products`.`price` = 900000 AND `products`.`code` = '72486' LIMIT 1
  # TRANSACTION (0.1ms)  BEGIN                                                                                                                                             
  # Product Exists? (0.2ms)  SELECT 1 AS one FROM `products` WHERE `products`.`code` = '72486' LIMIT 1

  # IF PRODUCT DOESN'T EXISTS, ACTIVE RECORD WILL CREATE IT
  # "Un nuevo producto será añadido a almacen"
  # Product Create (0.3ms)  INSERT INTO `products` (`title`, `code`, `stock`, `price`, `uuid`, `created_at`, `updated_at`) VALUES ('Green Tshirt', '72486', 40, 900000, '679f3776-1704-4a12-bb80-2fe88c79a151', '2022-04-22 04:58:50.536904', '2022-04-22 04:58:50.536904')                                                                                                    
  # "Un nuevo producto fue añadido a almacén Green Tshirt - 9000.0 USD"
  # TRANSACTION (52.4ms)  COMMIT
  # Product id: 56, title: "Green Tshirt", code: "72486", stock: 40, price: 900000, uuid: "679f3776-1704-4a12-bb80-2fe88c79a151", created_at: "2022-04-22 04:58:50.536904000 +0000", updated_at: "2022-04-22 04:58:50.536904000 +0000">

SCOPES

Los scopes son peticiones predefinidas las cuales se especifican dentro del modelo. Este método crea un método de clase, por lo que no es necesario instanciar un objeto para hacer nuestra petición a la base de datos. Una consideración importante de los scopes es que deben de respetar el PRINCIPIO DE RESPONSABILIDAD ÚNICA, porque los mismos podrán ser concatenados entre ellos para responder la información que requiere el cliente.

  # Scopes
  scope :availables, -> { where('stock > 0') }
  scope :cheaper_products, -> ( precio ) { where('price < ?', precio) }
  scope :ordered_by_price, -> { order(price: :desc) }

Después, puedes testearlo en la consola:

  products = Product.available # SELECT `products`.* FROM `products` WHERE (stock >= '1')
  products.size # => 57

  products = Product.available_maximium_products 10 # SELECT `products`.* FROM `products` WHERE (stock <= '10')
  products.size # => 24

  # Concatenated request using scopes
  products = Product.available_maximium_products(10).order_price_desc
  # or 
  products = Product.available_and_order_price_desc # Product Load (0.4ms)  SELECT `products`.* FROM `products` WHERE (stock >= '1') ORDER BY `products`.`price` DESC
  products.size # 57

MÉTODOS DE CLASE

Los scopes no deben de ser utilizados para realizar comparaciones a través de registros, pero con los métodos de clase, podemos solicitar, construir y establecer consultas importantes y complejas que requieran interacción con los registros que estemos obteniendo.

  # app/models/product.rb

  def self.top_five_available
    self.available.order_price_desc.limit(5).select(:title, :price)
  end

Y en la consola:

  products = Product.top_five_available # Product Load (0.4ms)  SELECT `products`.`title`, `products`.`price` FROM `products` WHERE (stock >= '1') ORDER BY `products`.`price` DESC LIMIT 5
  products.size # 5 records

  # <Product:0x00007f93cff03880 id: nil, title: "Lightweight Paper Table", price: 9953368>,
  # <Product:0x00007f93cfef4718 id: nil, title: "Synergistic Leather Shoes", price: 9944133>,
  # <Product:0x00007f93cfef4650 id: nil, title: "Practical Rubber Bench", price: 9914656>,
  # <Product:0x00007f93cfef4588 id: nil, title: "Durable Leather Watch", price: 9733173>,
  # <Product:0x00007f93cfef44c0 id: nil, title: "Durable Linen Shirt", price: 9374968>

ACTUALIZAR REGISTROS

Para actualizar los registros en Rails, tenemos diferentes métodos para hacerlo dependiendo de los escenarios.

MÉTODO SAVE

  product = Product.find 14
  product.name = 'Flashing Lights'
  product.save

  # TRANSACTION (0.2ms)  BEGIN
  Product Update (1.9ms)  UPDATE `products` SET `products`.`title` = 'Flashing Lights', `products`.`uuid` = 'a1d1edf5-09a8-43b5-ba02-d3a8efbd6d81', `products`.`updated_at` = '2022-05-30 01:21:27.244663' WHERE `products`.`id` = 14 
  # TRANSACTION (2.0ms)  COMMIT 

MÉTODO UPDATE

(update)

Para utilizar el método update, tenemos que tener un objeto instanciado para que podamos actualizar sus campos.

  product = Product.first
  product.update title: 'iMac Pro', stock: 20, price: 500000
  # TRANSACTION (0.2ms)  BEGIN
  Product Update (0.4ms)  UPDATE `products` SET `products`.`title` = 'iMac Pro', `products`.`code` = 'P45099', `products`.`stock` = 20, `products`.`price` = 500000, `products`.`uuid` = '9fa8233c-62f5-4e08-9a78-f63e2722451d', `products`.`updated_at` = '2022-05-30 01:55:00.937094' WHERE `products`.`id` = 1
  # TRANSACTION (2.0ms)  COMMIT 

Este método únicamente correrá únicamente si pasa todas las validaciones del modelo. Si existe alguna validación que haya tenido un problema y por ello, obtengamos una excepción, obtendremos un rollback, que significa que el registro no pudo ser guardado.

  product.update stock: 0

  # TRANSACTION (0.1ms)  BEGIN
  # Product Exists? (0.4ms)  SELECT 1 AS one FROM `products` WHERE `products`.`code` = 'P45099' AND `products`.`id` != 1 LIMIT 1
  TRANSACTION (0.2ms)  ROLLBACK

  product.errors.messages
  # {:stock=>["El stock debe ser mayor a 0"]}

MÉTODO UPDATE ATTRIBUTE

(update_attribute)

A través de este método, nosotros seremos capaces de modificar solo ÚN campo de nuestro objeto instanciado. Este método evita las validaciones, por lo que es necesario usar este comando cuidadosamente.

  product = Product.last
  product.update_attribute :title, "Tshirt Real Madrid Season"
  # UPDATE `products` SET `products`.`title` = 'Tshirt Real Madrid Season 22-23', `products`.`uuid` = '2d563cea-1d09-4f30-bf80-9dc7a79ddd91', `products`.`updated_at` = '2022-05-30 02:35:10.471173' WHERE `products`.`id` = 57

  product.update_attribute :stock, 0
  # UPDATE `products` SET `products`.`stock` = 0, `products`.`uuid` = '0473fdda-3e6f-4c74-8f09-0e80a8c2ec0e', `products`.`updated_at` = '2022-05-30 02:36:28.366937' WHERE `products`.`id` = 57

MÉTODO UPDATE ATTRIBUTES

(update_attributes)

Método en desuso por método de update

A diferencia del comando update_attribute, el comando update_attributes solo se ejecuta si todas las validaciones se realizan correctamente.

  product = Product.second
  product.update_attributes title: 'iPad Pro', stock: 0, code: "P55204"

MÉTODO UPDATE!

(update!)

Este método arroja una excepción cuando la modificación del registro es inválida.

  product.update! stock: 0
  # /Users/williamromero/.rvm/gems/ruby-3.0.0/gems/activerecord-7.0.2.2/lib/active_record/validations.rb:80:in `raise_validation_error': Validation failed: Stock El stock debe ser mayor a 0 (ActiveRecord::RecordInvalid)

MÉTODO UPDATE COLUMN

(update_column)

Este método nos permite la modificación de una columna para nuestra tabla y las validaciones y callbacks serán omitidas. También, la columna updated_at no será actualizado.

  product = Product.last
  # Product Load (0.4ms)  SELECT `products`.* FROM `products` ORDER BY `products`.`id` DESC LIMIT 1

  product.updated_at
  # Wed, 08 Jun 2022 17:10:33.625950000 UTC +00:00 

  product.update_column :stock, 10
  # Product Update (45.8ms)  UPDATE `products` SET `products`.`stock` = 10 WHERE `products`.`id` = 800

  product.updated_at
  # Wed, 08 Jun 2022 17:10:33.625950000 UTC +00:00 

Este método solo debe de ser usado si el performance de respuesta es algo crítico debido a que se gana velocidad pero se pierde certeza o seguridad.

MÉTODO UPDATE COLUMNS

(update_columns)

Mismo que le método update_column pero se pueden actualizar múltiples atributos.

  product = Product.last
  product.update_columns stock: 10, code: 'P00241'

  Product Update (12.3ms)  UPDATE `products` SET `products`.`stock` = 10, `products`.`code` = 'P00241' WHERE `products`.`id` = 57
  # => true

DESTRUIR RECORDS

MÉTODO DESTROY

El siguiente código es usado para remover productos individualmente.

  product = Product.last
  product.destroy # This destroy the object

MÉTODO DESTROY_ALL

El siguiente código, va a ser utilizado para remover múltiples productos.

  Product.where("stock <= ?", 2).destroy_all
  # Product Load (0.5ms)  SELECT `products`.* FROM `products` WHERE (stock <= '2')
  # TRANSACTION (0.2ms)  BEGIN                                                                         
  Product Destroy (1.5ms)  DELETE FROM `products` WHERE `products`.`id` = 26                         
  # TRANSACTION (2.0ms)  COMMIT                                                                        
  # TRANSACTION (0.3ms)  BEGIN                                                                         
  Product Destroy (0.6ms)  DELETE FROM `products` WHERE `products`.`id` = 37                         
  # TRANSACTION (1.0ms)  COMMIT                                                                        
  # => [#<Product>,<Product>]

  # This destroy the records

MONITOREAR CAMBIOS

  product = Product.first

  # Revisar si alguno de los atributos que han cambiado

  product.changed?        # => false
  product.stock = 200     # 200
  product.changed?        # => true  

  product.changes         # => {"stock"=> [20, 200]}
  product.stock_changed?  # => true
  product.title_changed?  # => false

  # Revisa el valor previo cuando el atributo ha sido cambiado
  product.stock_was       # => 20

  product.save
  # Product Update (0.9ms)  UPDATE `products` SET `products`.`stock` = 200, `products`.`uuid` = 'b60caeb2-4240-477d-b42e-fd680745f721', `products`.`updated_at` = '2022-05-30 05:54:56.501482' WHERE `products`.`id` = 1

  product.changed?        # => {} El estado retorna el estado inicial y cambios si el mismo no se ha persistido.

USAR MÉTODOS PARA MONITOREAR EL STATUS DE LOS ATRIBUTOS

  class Product < ApplicationRecord
    # Validations
    before_update :code_notification_changed, if: :code_changed?
    # Following code

    def code_changed?
      puts "\n\n\n >>>>> El código ha sido modificado <<<<< \n\n\n"
    end
  end

Ahora si el atributo código es cambiado:

  product = Product.last
  product.code = "P20939"
  product.save

  # TRANSACTION (0.2ms)  BEGIN
  # Product Exists? (0.4ms)  SELECT 1 AS one FROM `products` WHERE `products`.`code` = 'P20939' AND `products`.`id` != 57 LIMIT 1
  # "Un nuevo producto será añadido a almacen"                               
                                                                                                                                              
  El código ha sido modificado                                  
                                                               
                                                               
  #Product Update (0.6ms)  UPDATE `products` SET `products`.`code` = 'P20939', `products`.`uuid` = '1698fba4-6918-4ae7-9634-9c89fe04f00d', `products`.`updated_at` = '2022-05-30 05:58:58.511540' WHERE `products`.`id` = 57           
  # "Un nuevo producto fue añadido a almacén Tshirt Real Madrid Season 22-23 - 5000.0 USD"
  # TRANSACTION (2.0ms)  COMMIT 

Cuando el registro es persistido

  product = Product.first
  product.stock = 400
  product.save

  product.saved_change_to_stock? # => true
  product.saved_change_to_title? # => false

Aquí un particular ejemplo de callbacks:

  class Product < ApplicationRecord
    # Validations
    after_update  :send_notification_stock, if: :stock_limit? 
    # Following code

    private

    def stock_limit?
      self.saved_change_to_stock? && stock <= 5 
    end
  end

Ahora, si nosotros editamos el stock de nuestro producto:

  product = Product.first
  product.stock = 2
  product.save

  #TRANSACTION (0.3ms)  BEGIN
  #Product Exists? (0.4ms)  SELECT 1 AS one FROM `products` WHERE `products`.`code` = 'P45099' AND `products`.`id` != 1 LIMIT 1
  # "Un nuevo producto será añadido a almacen"        
                                                                                               
  # >>>>> El código ha sido modificado <<<<<        
                                                                                              
  # Product Update (0.4ms)  UPDATE `products` SET `products`.`stock` = 2, `products`.`uuid` = '7578fe4f-f10a-430f-b841-fcd1f93ba8b1', `products`.`updated_at` = '2022-05-30 06:16:19.572856' WHERE `products`.`id` = 1    
                                                 
  >>>>> El producto iMac Pro se encuentra escaso en almacen: 2 <<<<< 


  # "Un nuevo producto fue añadido a almacén iMac Pro - 5000.0 USD"
  # TRANSACTION (1.4ms)  COMMIT
  # => true

Referencias

Loading for Inspect message when records are not loaded

How to count with active record

ActiveRecord::Association Object Count`

ActiveRecord database performance improvement

Differences between ActiveRecord & ActiveRecord::Relation object

Difference of ActiveRecord::Relation object

Get just the first limited record of a Relation with PICK method

⚠️ **GitHub.com Fallback** ⚠️