2009-02-28 3 views
82

Я хочу проверить дату в моей модели в Ruby on Rails, однако значения дня, месяца и года уже конвертируются в неправильную дату к моменту, когда они достигают моей модели.Как проверить дату в рельсах?

Например, если я ввожу 31 февраля 2009 года, на мой взгляд, когда я использую в своем контроллере Model.new(params[:model]), он преобразует его в «3 марта 2009», который моя модель затем видит в качестве допустимой даты, какой она есть, но это неверно.

Я хотел был бы сделать эту валидацию в моей модели. Есть ли способ, которым я могу, или я об этом совершенно не прав?

Я нашел этот «Date validation», который обсуждает проблему, но он никогда не был разрешен.

+1

Этот вопрос в настоящее время один из лучшие результаты Google для проверки даты Rails. Вы можете пропустить это на первом проходе, как я сделал: важной частью выбранного ответа является камень validates_timeliness. Я даже не прочитал остальную часть ответа, потому что узнал о validates_timeliness где-то в другом месте, и этот камень делает все, что мне нужно, без необходимости писать какой-либо пользовательский код. –

ответ

55

Я предполагаю, что вы используете вспомогательный помощник date_select для создания тегов для даты. Другой способ, которым вы могли бы это сделать, - использовать помощник формы формы для полей day, month, year. Как это (например, я использовал это поле даты 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 %> 

А в модели, вы проверяете дату:

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 

Если вы ищете решение, плагин, я бы проверьте плагин validates_timeliness. Вот как это работает (со страницы 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 

Список методов проверки имеющихся в следующем:

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

Предлагаемое решение не работает для меня, и я использую Rails 2.3.5 – Roger

+0

Я переписал его, чтобы быть чище, и протестировал его против Rails 2.3.5, и это работает для меня. –

+0

Привет, Джек, я пробовал ваше решение, он работает для меня, добавляя attr_accessible: day,: month,: year. Для меня это не имеет смысла ... Спасибо, если у вас есть идеи! – benoitr

2

Так как вам нужно обработать строку даты, прежде чем она преобразуется в дату в вашей модели, я бы переопределить аксессор для этого поля

Допустим, ваше поле дата published_date. Добавьте это к вашей модели объекта:

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

Я не знаю, буду ли я использовать это для этой цели, но это отличное предложение. –

-3

Вы пробовали validates_date_time плагин?

+0

Хотя эта ссылка может ответить на вопрос, лучше включить здесь основные части ответа и предоставить ссылку для справки. Ответные ссылки могут стать недействительными, если связанная страница изменится. – Pinal

+0

Мне больше нравится мой ответ. Соглашаются два человека. –

+3

@Pinal Ссылка действительно мертва. –

19

Используя хроническую перл:

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

В этом конкретном примере мне пришлось использовать 'Chronic.parse (from_date_before_type_cast)', чтобы получить введенное строковое значение. В противном случае Rails будет выводить строку с 'to_date', так как поле было полем' datetime'. – Calvin

17

Если вы хотите Совместимость с Rails 3 или Ruby 1.9 попробует камень date_validator.

+0

Я просто попробовал это. Взял все 2 минуты, чтобы установить и добавить проверку! – jflores

9

Активная запись дает вам атрибуты _before_type_cast, которые содержат исходные данные атрибута перед приведением типов. Это может быть полезно для возврата сообщений об ошибках с заранее заданными значениями или просто для проверки, которые невозможны после приведения типов.

Я бы уклонился от предложения Даниэля фон Фанга о переопределении доступа, поскольку проверка в аксессуаре слегка изменила контракт на доступ. В этой ситуации активная запись имеет функцию явно. Используй это.

1

Вот нехронический ответ ..

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

Это всегда возвращает false: '' 1/1/2013 ".is_a? (Date)' - Дата исходит от пользовательского ввода, поэтому она подается в виде строки. Я должен сначала разобрать его как дату? – Mohamad

+0

Исправить. 'Date.parse (« 1/1/2013 »). Is_a? (Date)', но вы могли видеть, что если он вообще не анализирует, его также, вероятно, не является датой. – Trip

+1

Нужно спасти ошибку аргумента ... ahh, было бы здорово, если бы он просто разбирал строку автоматически ... ну, ну, для этого есть жемчужина. – Mohamad

0

Вы можете проверить дату и время, как так (в методе где-то в вашем контроллере с доступом к вашему Params, если вы используете пользовательский выбираете) .. ,

# 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

Немного поздно здесь, но благодаря «How do I validate a date in rails?» мне удалось написать этот валидатор, надежда полезна кому-то:

Внутри вашего 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 
Смежные вопросы