« Previous -
Version 27/76
(diff) -
Next » -
Current version
Guillermo Gómez, 04/05/2012 07:24 am
ActiveRecord validaciones¶
¿Cúando ocurren las validaciones?¶
Hay dos tipos de objetos Active Records, aquellos que corresponden a una fila dentro de la base de datos y aquellos que no. Cuando se crea un objeto fresco, por ejemplo usando el método new, dicho objeto no pertenece a la base de datos aún. Una vez que llame al método save, entonces será salvado apropiadamente en la base de datos en su respectiva tabla. Active Record usa el método de instancia new_record? para determinar si un objeto ya se encuentra en la base de datos o no.
El crear y salvar un nuevo registro ejecutará una operaciń SQL INSERT en la base de datos. Actualizar un registro existente ejecutará una operación SQL UPDATE.
Las validaciones son típicamente ejecutadas antes que dichos comandos sean enviados a la base de datos. Si cualquier validación falla, el objeto será marcado como inválido y Active Record no realizará la operación INSERT o UPDATE correspondiente. Esto evita almacenar objetos inválidos en la base de datos. Usted puede escoger el tener validaciones específicas a ser ejecutadas cuando el objeto es creado, salvado o actualizado.
Existen muchas formas de cambiar el estado de un objeto en la base de datos, algunos métodos disparan las validaciones, pero algunos no. Esto significa que es posible salvar un objeto en la base de datos en un estado inválido si usted no tiene cuidado.
Los siguientes métodos activan las validaciones y salvarán el objeto en la base de datos sólo si el objeto es válido:
- create
- create!
- save
- save!
- update
- update_attributes
- update_attributes!
Un ejemplo simple.
1class Persona < ActiveRecord::Base
2end
>> p = Persona.new
#<Persona id: nil, nombres: nil, apellidos: nil, cedula: nil, correo: nil, cargo: nil, created_at: nil, updated_at: nil>
>> p.new_record?
true
>> p.save
true
>> p.new_record?
false
Evitando las validaciones¶
Los siguientes métodos evitan las validaciones y salvarán el objeto en la base de datos sin importar su validez. Deben ser utilizados con precaución.
- decrement!
- decrement_counter
- increment!
- increment_counter
- toggle!
- touch
- update_all
- update_attribute
- update_column
- update_counters
Note que save también tiene la habilidad de evitar las validaciones si se le pasa el argumento :validate => false. Esta técnica igualmente debe usarse con cuidado.
save(:validate => false)
valid? e invalid?¶
Para verificar si un objeto es válido o no, Rails usa el método valid?. También usted puede usarlo libremente. valid? activa las validaciones y devuelve true si no hay errores en el objeto, y false de cualquier otra forma.
1class Persona < ActiveRecord::Base
2 validates :nombres, :presence => true
3end
>> Persona.create(:nombres => "Juan Bimba").valid?
true
>> Persona.create(:nombres => "").valid?
false
Después de que Active Record ha ejecutado las validaciones, cualquier error encontrado estará disponible por medio del método de instancia errors que devuelve una conjunto de errores. Por definición un objeto es válido si dicho conjunto está vacío después de ejecutar las validaciones.
Note que un objeto instanciado con new no reportará errores incluso si técnicamente es inválido porque las validaciones no se ejecutando cuando se usa new.
>> p = Persona.new
#<Persona id: nil, nombres: nil, apellidos: nil, cedula: nil, correo: nil, cargo: nil, created_at: nil, updated_at: nil>
>> p.errors
#<ActiveModel::Errors:0xb3742594 @messages=#<OrderedHash {}>, @base=#<Persona id: nil, nombres: nil, apellidos: nil, cedula: nil, correo: nil, cargo: nil, created_at: nil, updated_at: nil>>
>> p.valid?
false
>> p.errors
#<ActiveModel::Errors:0xb3742594 @messages=#<OrderedHash {:nombres=>["can't be blank"]}>, @base=#<Persona id: nil, nombres: nil, apellidos: nil, cedula: nil, correo: nil, cargo: nil, created_at: nil, updated_at: nil>>
>> p.save
false
>> p.save!
ActiveRecord::RecordInvalid: Validation failed: Nombres can't be blank
>> Persona.create!
ActiveRecord::RecordInvalid: Validation failed: Nombres can't be blank
invalid? es simplemente el inverso de valid?. invalid? activa sus validaciones devolviendo true si se consiguen errores y false si no.
errors[]¶
Para verificar si un atributo en particular es válido o no, usted puede usar errors[:atributo]. Ello le devuelve un arreglo de todos los errores para :atributo. Si no hay errores para el atributo especificado, se devuelve un arreglo vacío.
Este método sólo es útil después de ejecutar las validaciones ya que se inspecciona la colección errors y no dispara las validaciones él mismo. Es diferente del método ActiveRecord::Base#invalid? porque no verifica la validez del objeto como un todo. Sólo verifica si hay errores sobre un atributo individual del objeto.
1class Persona < ActiveRecord::Base
2 validates :nombres, :presence => true
3end
>> Persona.create.errors[:nombres]
["can't be blank"]
>> Persona.create.errors[:nombres].any?
true
Trabajaremos más acerca de los errores de validación más adelaten por ahora enfoquemos en los ayudantes incluídos por Rails para validación por omisión.
Ayudantes para validación¶
Active Record ofrece muchos ayudantes predefinidos para validación que puede usar directamente dentro de las definiciones de clase. Dichos ayudantes proveen reglas comunes de validación. Cada vez que una validación falla, se agrega un mensaje de error a la colección errors del objeto y dicho mensaje es asociado con el atributo que se está validando.
Cada ayudanete acepta un número arbitrario de nombres de atributos así que en una única línea de código usted puede agregar el mismo tipo de validación para varios atributos.
Todos estos ayudantes para validación aceptan las opciones :on y :message que definen cuándo ejecutar la validación y cuál mensaje agregar a la colección errors si falla. La opción :on toma uno de los valores :save (por omisión), :create o :update. Hay un mensaje de error por omisión para cada uno de los ayudantes. Dichos mensajes son utilizados cuando no se especifica la opción :message.
A continuación algunos de los ayudantes de validación más comunes.
acceptance¶
Valida que un checkbox en la interfase del usaurio ha sido marcada cuando se envió el formulario. Esto es típicamente utilizado cuando el usuario necesita haber aceptado los términos de uso de su aplicación, confirmar haber leído un texto, o cualquier otro concepto similar. Esta validación es muy específica a las aplicaciones web y esta aceptación no necesita ser registrada en su base de datos (si usted no tiene un campo para ello, el ayudante simplemente crea un atributo virtual).
1class Persona < ActiveRecord::Base
2 validates :terms_of_service, :acceptance => true
3end
El mensaje por omisión para este ayudante es “must be accepted”.
Puede recibir la opción :accept que determina el valor que será considerado como aceptación. Por omisión es "1" y puede ser fácilmente ajustado.
1class Persona < ActiveRecord::Base
2 validates :terms_of_service, :acceptance => { :accept => 'si' }
3end
validates_associated¶
Usted debería usar este ayudante cuando su modelo tiene asociaciones con otros modelos que también necesitan ser validados. Cuando intenta salvar el objeto, valid? será llamado sobre cada uno de los modelos asociados..
1class Library < ActiveRecord::Base
2 has_many :books
3 validates_associated :books
4end
Esta validación funcionará con todos los tipos de asociaciones.
No use validates_associated en ambos extremos de la asociación. Ellos pueden llamarse mutuamente y causar un lazo infinito.
El mensaje de error por omisión es "is invalid". Note que cada objeto asociado contrendrá su propia colección errors, errors no inflan al modelo que llama.
confirmation¶
Usted debería usar este ayudante cuando tenga dos campos de texto que deban tener exactamente el mismo contenido. Por ejemplo, puede que quiera confirmar una dirección email o una contraseña. Esta validación crea un atributo virtual cuyo nombre es el nombre del campo a confirma con el sufijo "_confirmation" agregado.
1class Person < ActiveRecord::Base
2 validates :email, :confirmation => true
3end
En su plantilla de vista podría usar algo como:
1<%= text_field :person, :email %>
2<%= text_field :person, :email_confirmation %>
Esta verificación se realiza sólo si email_confirmation es no nil. Para requirir información, asegúrese de agregar la verificación de presnecia para el atributo a confirmar.
1class Person < ActiveRecord::Base
2 validates :email, :confirmation => true
3 validates :email_confirmation, :presence => true
4end
El mensaje de error por omisión para este ayudante es "doesn’t match confirmation".
exclusion¶
Este ayudanete valida los valores de los atributos por si no están incluídos en un conjunto dado. De echo dicho conjunto puede ser cualquier objeto enumerable
1class Account < ActiveRecord::Base
2 validates :subdomain, :exclusion => { :in => %w(www us ca jp),
3 :message => "Subdomain %{value} is reserved." }
4end
El ayudante exclusion tiene una opción :in que recibe el conjunto de valores que no serán aceptados para los atributos validados. La opción :in tiene el alias :within que puede usar para el mismo propósito si lo desea. Este ejemplo usa la opción :message para mostrar como puede incluir el valor del atributo.
El valor de error por omisión es "is reserved".
format¶
Este ayudante valida los valores de atributos probando si concuerdan con una expresión regular dada que se especifica con la opción :with.
1class Product < ActiveRecord::Base
2 validates :legacy_code, :format => { :with => /\A[a-zA-Z]+\z/,
3 :message => "Only letters allowed" }
4end
El mensaje de error por omisión es "is invalid".
inclusion¶
Este ayudante valida que los valores de los atributos estén dentro de un conjunto dado. De hecho el conjunto puede ser cualquier objeto enumerable.
1class Coffee < ActiveRecord::Base
2 validates :size, :inclusion => { :in => %w(small medium large),
3 :message => "%{value} is not a valid size" }
4end
El ayudante de validación inclusion tiene la opciń :in que recibe el conjunto de valores que serán aceptables. La opción :in tiene el alias :within que puede usar para el mismo propósito. El ejemplo previo usa la opciń :message para mostrar cómo incluir el valor del atributo.
El mensaje de error por omisión es "is not included in the list".
length¶
Este ayudante valida la longitud de los valores de los atributos. Tiene una serie de opciones así que puede especificar las limitaciones de longitud de diferentes maneras
1class Person < ActiveRecord::Base
2 validates :name, :length => { :minimum => 2 }
3 validates :bio, :length => { :maximum => 500 }
4 validates :password, :length => { :in => 6..20 }
5 validates :registration_number, :length => { :is => 6 }
6end
Las posibles limitaciones en length son:
- :minimum – El atributo no puede tener menos de la cantidad especificada.
- :maximum – El atributo no puede tener más de la cantidad especificada.
- :in (o :within) – La longitud del atributo debe estar incluída en el intervalo dado. El valor para esta opción debe ser un rango.
- :is – La longitud del atributo debe ser igual al valor dado.
El mensaje de error por omisión depende del tipo de validación de longitud se haya probadoa. Usted puede personalziar dichos mensajes utilizando las opciones :wrong_length, :too_long, y :too_short options y el contenedor %{count} para identificar la cantidad correspondiente en uso. También puede usar la opción :message para especificar el mensaje de error.
1class Person < ActiveRecord::Base
2 validates :bio, :length => { :maximum => 1000,
3 :too_long => "%{count} characters is the maximum allowed" }
4end
Este ayudante cuenta caracteres por omisión, pero puede dividir el valor en formas diferentes usando la opción :tokenizer.
1class Essay < ActiveRecord::Base
2 validates :content, :length => {
3 :minimum => 300,
4 :maximum => 400,
5 :tokenizer => lambda { |str| str.scan(/\w+/) },
6 :too_short => "must have at least %{count} words",
7 :too_long => "must have at most %{count} words"
8 }
9end
Note que el mensaje de error por omisión es plural (e.g., “is too short (minimum is %{count} characters)”). Por esta razón cuando :minimum es 1 usted debería proveer un mensaje personalizado o usar a cambio validates_presence_of. Cuando :in o :within tiene un límite menor a 1, usted debería proveer un mensaje personalizado o llamar presence antes de a length.
El ayudante size es un alias para length.
numericality¶
Este ayudante valida que los atributos sólo tengan valores numéricos. Por omisión concordará con un signo opcional seguido de un entero o número de punto flotante. Para especificar que sólo se permiten números enteros, use la :only_integer puesta a true.
Si pone :only_interger en true, entonces se utilizará la expresión regular:
/\A[+-]?\d+\Z/
para validar el valor del atributo. Si no es así, intentará convertir el valor a número usando Float. Note que la expresión regular permite el caracter nueva líena al final.
1class Player < ActiveRecord::Base
2 validates :points, :numericality => true
3 validates :games_played, :numericality => { :only_integer => true }
4end
Además de :only_integer este ayudante también acepta las siguientes opciones para agregar limitaciones a los valores aceptables:
- :greater_than – Especifica que el valor debe ser mayor al valor dado. El mensaje de error por omisión es "must be greater than %{count}".
- :greater_than_or_equal_to – Especifica que el valor debe ser mayor o igual que al valor dado. El mensaje de error por omisión es "must be greater than or equal to %{count}”.
- :equal_to – Especifica que el valor debe ser igual al valor dado. El mensaje de error por omisiń es "must be equal to %{count}".
- :less_than – Especifica que el valor debe ser menor que el valor dado. El mensaje de error por omisión es "must be less than %{count}".
- :less_than_or_equal_to – Especifica que el valor debe ser menor o igual que el valor dado. El mensaje de error por omisión es "must be * less than or equal to %{count}".
- :odd – Especifica que el valor debe ser un valor impar si está puesta en true. El mensaje de error por omisión es "must be odd".
- :even – Esepcifica que el valor debe ser un valor par si está puesta en true. El mensaje de error por omisión es "must be even".
El mensaje de error por omisión es "is not a number".
presence¶
Este ayudante valida que los atributos especificados no estén vacíos. Usa el método blank? para verificar el valor, ya sea nil o un string en blanco, es decir, vacío o que tenga espacios.
1class Person < ActiveRecord::Base
2 validates :name, :login, :email, :presence => true
3end
Si desea asegurarse que una asociación está presente, deberá probar que la llave foránea está presente y no el objeto mismo asociado.
1class LineItem < ActiveRecord::Base
2 belongs_to :order
3 validates :order_id, :presence => true
4end
Ya que false.blank? es true, si desea validar la presencia de un campo booleano usted debería usar validates :field_name, :inclusion => { :in => [true, false] }.
El mensaje de error por omisión es "can’t be empty".
uniqueness¶
Este ayudante valida que el valor del atributo es único justo antes de que el objeto sea salvado. No crea una limitación de ser único en la base de datos así que puede pasar que dos conexiones diferentes cree dos registros con el mismo valor. Para evitar eso, usted debe crear un índice único en su base de datos.
1class Account < ActiveRecord::Base
2 validates :email, :uniqueness => true
3end
Esta validación ocurre con la ejecución de una consulta SQL en la tabla del modelo, buscando algún registro existente con el mismo valor en dicho atributo.
Existe la opción :scope que puede usar para especificar otros atributos a usar para limitar la verificación de ser único:
1class Holiday < ActiveRecord::Base
2 validates :name, :uniqueness => { :scope => :year,
3 :message => "should happen once per year" }
4end
Existe también la opción :case_sensitive que puede usar para definir si la limitación es sensible al mayusculado o no. Esta opción es por omisión true.
1class Person < ActiveRecord::Base
2 validates :name, :uniqueness => { :case_sensitive => false }
3end
Note que algunas bases de datos son configuradas para realizar búsquedas insensibles al mayusculados de todas maneras.
El mensaje de error por omisión es "has already been taken".
validates_with¶
Este ayudante pasa el registro a una clase aparte para validación.
1class Person < ActiveRecord::Base
2 validates_with GoodnessValidator
3end
4
5class GoodnessValidator < ActiveModel::Validator
6 def validate(record)
7 if record.first_name == "Evil"
8 record.errors[:base] << "This person is evil"
9 end
10 end
11end
Los errores agregados a record.errors[:base] relaciona el estado del registro como un todo y no a un atributo específico.
El ayudante validates_with_helper toma una clase o una lista de clases para validación. No hay mensaje de error por omisión para validates_with. Usted debe manualmente agregar los errores a los errores de la colección en la clase validadora.
Para implementar el método validate usted debe tener definido un parámetro record que es el registro a validar.
Como todas las otras validaciones validate_with tiene las opciones :if, :unless y :on. Si usted pasa cualquier otra opción, se pasarán dichas opciones a la clase validadora como opciones.
1class Person < ActiveRecord::Base
2 validates_with GoodnessValidator, :fields => [:first_name, :last_name]
3end
4
5class GoodnessValidator < ActiveModel::Validator
6 def validate(record)
7 if options[:fields].any?{|field| record.send(field) == "Evil" }
8 record.errors[:base] << "This person is evil"
9 end
10 end
11end
