2009-02-28 21 views
82

Quiero validar una fecha en mi modelo en Ruby on Rails; sin embargo, los valores de día, mes y año ya se han convertido en una fecha incorrecta cuando llegan a mi modelo.¿Cómo valido una fecha en los rieles?

Por ejemplo, si ingreso el 31 de febrero de 2009 en mi opinión, cuando uso Model.new(params[:model]) en mi controlador, lo convierte en "3 de marzo de 2009", que mi modelo ve como una fecha válida, que es, pero es incorrecto

Me gustaría poder hacer esta validación en mi modelo. ¿Hay alguna manera que yo pueda, o estoy yendo sobre esto completamente equivocado?

Encontré este "Date validation" que discute el problema pero nunca fue resuelto.

+1

Esta pregunta es actualmente uno de los principales resultados de Google para la validación de la fecha de Rails. Puede que lo pierda en el primer pase como lo hice yo: la parte importante de la respuesta seleccionada es la gema validates_timeliness. Ni siquiera leí el resto de la respuesta porque me enteré de la validación en otra parte, y esa joya hace todo lo que necesito sin tener que escribir ningún código personalizado. –

Respuesta

55

Supongo que está utilizando el ayudante date_select para generar las etiquetas para la fecha. Otra forma de hacerlo es utilizar select form helper para los campos de día, mes y año. Así (ejemplo que utiliza es el campo de fecha created_at):

<%= f.select :month, (1..12).to_a, selected: @user.created_at.month %> 
<%= f.select :day, (1..31).to_a, selected: @user.created_at.day %> 
<%= f.select :year, ((Time.now.year - 20)..Time.now.year).to_a, selected: @user.created_at.year %> 

Y en el modelo, se valida la fecha:

attr_accessor :month, :day, :year 
validate :validate_created_at 

private 

def convert_created_at 
    begin 
    self.created_at = Date.civil(self.year.to_i, self.month.to_i, self.day.to_i) 
    rescue ArgumentError 
    false 
    end 
end 

def validate_created_at 
    errors.add("Created at date", "is invalid.") unless convert_created_at 
end 

Si usted está buscando una solución plug-in, que había presiona el plugin validates_timeliness. Funciona de la siguiente (en la página de GitHub):

class Person < ActiveRecord::Base 
    validates_date :date_of_birth, on_or_before: lambda { Date.current } 
    # or 
    validates :date_of_birth, timeliness: { on_or_before: lambda { Date.current }, type: :date } 
end 

La lista de métodos de validación disponibles son los siguientes:

validates_date  - validate value as date 
validates_time  - validate value as time only i.e. '12:20pm' 
validates_datetime - validate value as a full date and time 
validates   - use the :timeliness key and set the type in the hash. 
+0

La solución sugerida no funciona para mí y estoy usando Rails 2.3.5 – Roger

+0

Lo reescribí para ser más limpio, y lo probé contra Rails 2.3.5 y esto funciona para mí. –

+0

Hola Jack, he intentado con tu solución, me funciona para agregar attr_accessible: day,: month,: year. Lo cual no tiene sentido para mí ... ¡Gracias si tienes alguna idea! – benoitr

2

Ya que se necesita para manejar la cadena de fecha antes de que se convierte en una fecha en su modelo, me anulan el descriptor de acceso para ese campo

Digamos que su campo de fecha es published_date. Agregue esto a su objeto de modelo:

def published_date=(value) 
    # do sanity checking here 
    # then hand it back to rails to convert and store 
    self.write_attribute(:published_date, value) 
end 
+0

No sé si usaré esto para este propósito, pero es una gran sugerencia. –

-3

¿Ha probado el validates_date_time plug-in?

+0

Si bien este enlace puede responder a la pregunta, es mejor incluir las partes esenciales de la respuesta aquí y proporcionar el enlace de referencia. Las respuestas de solo enlace pueden dejar de ser válidas si la página vinculada cambia. – Pinal

+0

Me gusta más mi respuesta. Dos personas están de acuerdo. –

+3

@Pinal El enlace está realmente muerto ahora. –

19

El uso de la gema crónica:

class MyModel < ActiveRecord::Base 
    validate :valid_date? 

    def valid_date? 
    unless Chronic.parse(from_date) 
     errors.add(:from_date, "is missing or invalid") 
    end 
    end 

end 
+2

En este ejemplo específico, tuve que usar 'Chronic.parse (from_date_before_type_cast)' para obtener la entrada de valor de cadena. De lo contrario, Rails encasillaría la cadena con 'to_date' ya que el campo era un campo' datetime'. – Calvin

17

Si desea La compatibilidad de Rails 3 o Ruby 1.9 prueba la gema date_validator.

+0

Acabo de probar esto. ¡Tomó todos los 2 minutos para instalar y agregar validación! – jflores

9

Active Record le otorga _before_type_cast atributos que contienen los datos de atributo sin formato antes del encasillado. Esto puede ser útil para devolver mensajes de error con valores previos a la transmisión o simplemente hacer validaciones que no son posibles después de la conversión.

Me rehúso a la sugerencia de Daniel Von Fange de anular el acceso, porque hacer la validación en un acceso cambia el contrato de acceso ligeramente. Active Record tiene una función explícitamente para esta situación. Úselo.

1

Aquí es una respuesta no crónicos ..

class Pimping < ActiveRecord::Base 

validate :valid_date? 

def valid_date? 
    if scheduled_on.present? 
    unless scheduled_on.is_a?(Time) 
     errors.add(:scheduled_on, "Is an invalid date.") 
    end 
    end 
end 
+1

Esto siempre devuelve falso: '" 1/1/2013 ".is_a? (Date)' - La fecha proviene de la entrada del usuario, por lo que se alimenta como una cadena. ¿Tendría que analizarlo primero como una cita? – Mohamad

+0

Correcto. 'Date.parse (" 1/1/2013 "). Is_a? (Date)', pero se puede ver que si no se analiza en absoluto, probablemente tampoco sea una fecha. – Trip

+1

Necesito rescatar un error de argumento ... ahh, hubiera sido genial si hubiese analizado la cadena automáticamente ... bueno, hay gemas para eso. – Mohamad

0

Puede validar la fecha y hora como tal (en un método en algún lugar de su controlador de acceso a los parametros si está utilizando Selecciona Personal) .. .

# Set parameters 
year = params[:date][:year].to_i 
month = params[:date][:month].to_i 
mday = params[:date][:mday].to_i 
hour = params[:date][:hour].to_i 
minute = params[:date][:minute].to_i 

# Validate date, time and hour 
valid_date = Date.valid_date? year, month, mday 
valid_hour = (0..23).to_a.include? hour 
valid_minute = (0..59).to_a.include? minute 
valid_time = valid_hour && valid_minute 

# Check if parameters are valid and generate appropriate date 
if valid_date && valid_time 
    second = 0 
    offset = '0' 
    DateTime.civil(year, month, mday, hour, minute, second, offset) 
else 
    # Some fallback if you want like ... 
    DateTime.current.utc 
end 
0

Un poco tarde aquí, pero gracias a "How do I validate a date in rails?" me las arreglé para escribir este validador, la esperanza es útil para alguien:

Dentro de su model.rb

validate :date_field_must_be_a_date_or_blank 

# If your field is called :date_field, use :date_field_before_type_cast 
def date_field_must_be_a_date_or_blank 
    date_field_before_type_cast.to_date 
rescue ArgumentError 
    errors.add(:birthday, :invalid) 
end 
Cuestiones relacionadas