Come convalido una data in rails?


92

Voglio convalidare una data nel mio modello in Ruby on Rails, tuttavia, i valori di giorno, mese e anno sono già convertiti in una data errata nel momento in cui raggiungono il mio modello.

Ad esempio, se a mio avviso inserisco il 31 febbraio 2009, quando lo utilizzo Model.new(params[:model])nel controller, viene convertito in "3 marzo 2009", che il mio modello vede quindi come una data valida, che è, ma non è corretta.

Mi piacerebbe poter eseguire questa convalida nel mio modello. C'è un modo in cui posso, o sto andando su questo completamente sbagliato?

Ho trovato questa " convalida della data " che discute il problema ma non è mai stato risolto.


4
Questa domanda è attualmente uno dei migliori risultati di Google per la convalida della data di Rails. Potresti perderlo al primo passaggio come ho fatto io: la parte importante della risposta selezionata è la gemma validates_timeliness. Non ho nemmeno letto il resto della risposta perché ho scoperto validates_timeliness da qualche altra parte, e quella gemma fa tutto ciò di cui ho bisogno senza che io debba scrivere alcun codice personalizzato.
Jason Swett

Risposte:


61

Immagino che tu stia usando l' date_selecthelper per generare i tag per la data. Un altro modo per farlo è utilizzare l'helper del modulo di selezione per i campi giorno, mese, anno. In questo modo (l'esempio che ho usato è il campo data 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 %>

E nel modello convalidi la data:

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

Se stai cercando una soluzione plug-in, controlla il plug-in validates_timeliness . Funziona così (dalla pagina 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 

L'elenco dei metodi di convalida disponibili è il seguente:

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.

La soluzione suggerita non funziona per me e sto usando Rails 2.3.5
Roger

L'ho riscritto per renderlo più pulito e l'ho testato con Rails 2.3.5 e questo funziona per me.
Jack Chu

Ciao Jack, ho provato la tua soluzione, funziona per me aggiungendo attr_accessible: day,: month,: year. Il che non ha senso per me ... Grazie se hai un'idea!
benoitr

In Rails 3, sto usando github.com/adzap/validates_timeliness ed è fantastico.
Jason Swett

Il plugin / gem validates_timeliness è davvero bello da usare. Funziona un fascino e mantiene il mio codice più piccolo. Grazie per il consiglio.
mjnissim

20

Usando la gemma cronica:

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

3
In questo esempio specifico, ho dovuto utilizzare Chronic.parse(from_date_before_type_cast)per ottenere l'input del valore della stringa. Altrimenti Rails digiterebbe la stringa con to_datepoiché il campo era un datetimecampo.
Calvin

18

Se vuoi la compatibilità con Rails 3 o Ruby 1.9 prova il gem date_validator .


Ho appena provato questo. Sono stati necessari 2 minuti per l'installazione e l'aggiunta della convalida!
jflores

11

Active Record ti fornisce _before_type_castattributi che contengono i dati grezzi degli attributi prima del typecasting. Questo può essere utile per restituire messaggi di errore con valori pre-typecast o semplicemente per eseguire convalide che non sono possibili dopo il typecast.

Eviterei il suggerimento di Daniel Von Fange di sovrascrivere l'accessor, perché fare la convalida in un accessor cambia leggermente il contratto dell'accessor. Active Record ha una funzione esplicitamente per questa situazione. Usalo.


4

Poiché è necessario gestire la stringa della data prima che venga convertita in una data nel modello, sovrascriverei la funzione di accesso per quel campo

Supponiamo che il tuo campo data sia published_date. Aggiungi questo al tuo oggetto modello:

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

Non so se lo userò per questo scopo, ma è un ottimo suggerimento.
Dan Rosenstark

3

Un po 'tardi qui, ma grazie a " Come convalido una data in rails? " Sono riuscito a scrivere questo validatore, la speranza è utile a qualcuno:

Dentro il tuo 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

1

Ecco una risposta non cronica ..

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
Restituisce sempre false: "1/1/2013".is_a?(Date)- La data proviene dall'input dell'utente, quindi viene fornita come stringa. Dovrei prima analizzarlo come una data?
Mohamad

Corretta. Date.parse("1/1/2013").is_a?(Date), ma potresti vedere che se non analizza affatto, probabilmente non è nemmeno una data.
Viaggio

1
È necessario salvare l'errore dell'argomento ... ahh, sarebbe stato fantastico se analizzasse la stringa automaticamente ... vabbè, ci sono gemme per questo.
Mohamad

0

Puoi convalidare la data e l'ora in questo modo (in un metodo da qualche parte nel tuo controller con accesso ai tuoi parametri se stai usando selezioni personalizzate) ...

# 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

-3

Hai provato il validates_date_timeplug-in?


Sebbene questo collegamento possa rispondere alla domanda, è meglio includere le parti essenziali della risposta qui e fornire il collegamento come riferimento. Le risposte di solo collegamento possono diventare non valide se la pagina collegata cambia.
Pinal

Mi piace di più la mia risposta. Due persone sono d'accordo.
Ryan Duffield

4
@Pinal Il collegamento è effettivamente morto ora.
Wayne Conrad,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.