Chcę sprawdzić datę w moim modelu w Ruby on Rails, jednak wartości dnia, miesiąca i roku są już przekonwertowane na niepoprawną datę, zanim dotrą do mojego modelu.
Na przykład, jeśli moim zdaniem wprowadzę 31 lutego 2009 r., Kiedy używam Model.new(params[:model])
w kontrolerze, konwertuje to na „3 marca 2009 r.”, Który mój model postrzega jako prawidłową datę, która jest, ale jest niepoprawna.
Chciałbym móc przeprowadzić tę walidację w moim modelu. Czy jest jakiś sposób, w jaki mogę, czy też całkowicie się mylę?
Znalazłem to „ Sprawdzanie daty ”, które omawia problem, ale nigdy nie zostało rozwiązane.
ruby-on-rails
ruby
validation
date
Megamug
źródło
źródło
Odpowiedzi:
Domyślam się, że używasz
date_select
pomocnika do generowania tagów dla daty. Innym sposobem jest użycie pomocnika wyboru formularza dla pól dnia, miesiąca i roku. W ten sposób (przykład, którego użyłem, to pole created_at date):<%= 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 %>
W modelu sprawdzasz datę:
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
Jeśli szukasz rozwiązania wtyczki, skorzystałbym z wtyczki validates_timeliness . Działa to tak (ze strony 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
Lista dostępnych metod walidacji jest następująca:
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.
źródło
Korzystanie z chronicznego klejnotu:
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
źródło
Chronic.parse(from_date_before_type_cast)
, aby uzyskać wejściową wartość ciągu. W przeciwnym razie Railsy przerzuciłyby łańcuch na typ,to_date
ponieważ pole byłodatetime
polem.Jeśli chcesz kompatybilność z Rails 3 lub Ruby 1.9, wypróbuj gem date_validator .
źródło
Moduł Active Record podaje
_before_type_cast
atrybuty, które zawierają surowe dane atrybutów przed rzutowaniem. Może to być przydatne do zwracania komunikatów o błędach z wartościami przed typowaniem lub po prostu przeprowadzania walidacji, które nie są możliwe po rzutowaniu typu.Unikałbym sugestii Daniela Von Fange, aby zastąpić akcesor, ponieważ wykonanie walidacji w akcesorium nieznacznie zmienia kontrakt akcesora. Moduł Active Record ma funkcję wyraźnie dostosowaną do tej sytuacji. Użyj tego.
źródło
Trochę się spóźniłem, ale dzięki „ Jak sprawdzić datę w railsach? ” Udało mi się napisać ten walidator, nadzieja komuś się przyda:
Wewnątrz twojego
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
źródło
Ponieważ musisz obsłużyć ciąg daty, zanim zostanie przekonwertowany na datę w modelu, nadpisałbym akcesor dla tego pola
Powiedzmy, że Twoje pole daty to
published_date
. Dodaj to do obiektu modelu: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
źródło
Oto niechroniczna odpowiedź.
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
źródło
"1/1/2013".is_a?(Date)
- Data pochodzi z danych wprowadzonych przez użytkownika, więc jest podawana jako ciąg. Musiałbym najpierw przeanalizować to jako datę?Date.parse("1/1/2013").is_a?(Date)
, ale możesz zobaczyć, że jeśli w ogóle nie analizuje, prawdopodobnie również nie jest datą.Możesz zweryfikować datę i godzinę w ten sposób (w metodzie gdzieś w kontrolerze z dostępem do twoich parametrów, jeśli używasz niestandardowych selekcji) ...
# 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
źródło
Czy wypróbowałeś
validates_date_time
wtyczkę?źródło