2009-02-28 19 views
82

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 al momento in cui raggiungono il mio modello. Ad esempio, se entro il 31 febbraio 2009 a mio avviso, quando uso il Model.new(params[:model]) nel mio controller, lo converto in "3 marzo 2009", che il mio modello vede come data valida, che è, ma non è correttoCome convalidare una data nei binari?

Mi piacerebbe essere in grado di fare questa convalida nel mio modello. C'è un modo che posso, o sto andando su questo completamente sbagliato?

Ho trovato questo "Date validation" che illustra il problema ma non è mai stato risolto.

+1

Questa domanda è attualmente uno dei i 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 le validates_timeliness da qualche altra parte, e quella gemma fa tutto ciò di cui ho bisogno senza che io debba scrivere alcun codice personalizzato. –

risposta

55

Suppongo che tu stia utilizzando l'helper date_select per generare i tag per la data. Un altro modo in cui potresti farlo è usare l'aiutante di forma select per i campi giorno, mese, anno. Ti piace questa (esempio ho usato è il campo della 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, di convalidare 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 siete alla ricerca di una soluzione plug-in, mi piacerebbe controlla il plugin validates_timeliness. Funziona in questo modo (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 validazione disponibili sono i seguenti:

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 soluzione suggerita non funziona per me e sto usando Rails 2.3.5 – Roger

+0

L'ho riscritto per essere più pulito, e testato su Rails 2.3.5 e questo funziona per me. –

+0

Ciao Jack, ho provato la tua soluzione, funziona per me aggiungendo attr_accessible: giorno,: mese,: anno. Il che non ha senso per me ... Grazie se hai qualche idea! – benoitr

2

Dal momento che è necessario gestire la stringa della data prima di essere convertito in una data nel modello, mi piacerebbe sovrascrivono la funzione di accesso per quel campo

Diciamo che il campo della data è published_date. Aggiungi questo al vostro modello a oggetti:

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

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

-3

Hai provato il plug-in validates_date_time?

+0

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

+0

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

+3

@Pinal Il collegamento è davvero morto ora. –

19

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 
+2

In questo specifico esempio, ho dovuto usare 'Chronic.parse (from_date_before_type_cast)' per ottenere l'input del valore di stringa. Altrimenti Rails avrebbe tipizzato la stringa con 'to_date' poiché il campo era un campo' datetime'. – Calvin

17

Se si desidera Compatibile con Rails 3 o Ruby 1.9, prova la gemma date_validator.

+0

Ho appena provato questo. Ci sono voluti 2 minuti per installare e aggiungere la convalida! – jflores

9

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

Vorrei evitare il suggerimento di Daniel Von Fange di scavalcare l'accessor, perché la convalida in un accessor modifica leggermente il contratto di accesso. Active Record ha una funzione esplicitamente per questa situazione. Usalo.

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: '" 01/01/2013 ".is_a? (Data)' - La data proviene dall'input dell'utente, quindi viene alimentata come una stringa. Dovrei prima analizzare come data? – Mohamad

+0

corretto. 'Date.parse (" 1/1/2013 "). Is_a? (Date)', ma si potrebbe vedere che se non analizza affatto, probabilmente non è una data. – Trip

+1

Hai bisogno di salvare l'errore dell'argomento ... ahh, sarebbe stato fantastico se avesse analizzato la stringa automaticamente ... oh, beh, ci sono gemme per questo. – Mohamad

0

È possibile convalidare la data e l'ora in questo modo (in un metodo da qualche parte nel vostro controller con accesso ai vostri params se si utilizza personalizzato seleziona) .. .

# 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 po 'in ritardo qui, ma grazie a "How do I validate a date in rails?" Sono riuscito a scrivere questo validatore, la speranza è utile a qualcuno:

All'interno della vostra 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