Validaciones en Rails: ActiveRecord vs BD

  • por Thomas Vogt

Active Record es una herramienta muy poderosa de Rails que mapea modelos a tablas en la base de datos, lo que permite hacer consultas y validaciones de manera simplificada, entre otras cosas.

Por ejemplo, si es necesario que un usuario tenga un número de teléfono, basta con agregar una simple validación al modelo de usuario: 

class User < ActiveRecord::Base
  validates :phone_number, presence: true
end

Esto no genera ningún cambio en la base de datos y si en un futuro la lógica de la aplicación no requiere de un número de teléfono para cada usuario, basta con eliminar la validación del modelo.

Si se hubiese hecho la misma validación a nivel de base de datos y la presencia del número de teléfono ya no fuese requerida, sería necesario crear una migración en la base de datos para cambiar la columna.

¿Puede haber problemas al validar con Active Record?

Tomemos ahora por ejemplo un sistema de filas, en el que cada número de atención debe ser único.

La validación con Active Record se vería así:

class Ticket < ActiveRecord::Base
  validates_uniqueness_of :ticket_number
end

De esta forma se revisa al momento de guardar si el número de atención ya existe y de ser así no lo permite. Esta validación funciona en casos normales, pero puede tener problemas ante "race conditions".

En el caso en que se intenten crear dos tickets al mismo tiempo puede darse el siguiente problema:

Proceso 1 Proceso 2
Ticket.create(ticket_number: 1)  
  Ticket.create(ticket_number: 1)
SELECT en tickets -> no hay ticket con ticket_number=1  
  SELECT en tickets -> no hay ticket con ticket_number=1
INSERT en tickets  
  INSERT en tickets

De esta forma nos encontramos con dos tickets con el mismo número de atención.

¿Cómo evitarlo?

Rails permite también agregar validaciones en las migraciones de la base de datos.

Para el mismo ejemplo de los número de atención una validación a nivel de base de datos se vería así:

class AddValidation < ActiveRecord::Migration
  def up
    add_index :tickets, :ticket_number, unique: true
  end

  def down
    remove_index :tickets, column: :ticket_number
  end
end

De esta forma aseguramos la unicidad del número de atención incluso ante "race conditions".

En resumen

Active Record aporta una forma simple y rápida de agregar validaciones q​ue son suficientes para la mayoría de los casos. Sin embargo para casos en los que la concurrencia puede ser un problema se sugiere agregar validaciones a nivel de base de datos.

 

Referencias

http://nhw.pl/wp/2009/07/20/are-activerecord-validations-worth-anything

https://en.wikipedia.org/wiki/Race_condition

http://stackoverflow.com/questions/2367281/ruby-on-rails-is-it-better-to-validate-in-the-model-or-the-database

Imagen Principal