Rails date_select помощник и проверка - PullRequest
9 голосов
/ 21 июля 2010

У меня есть поле даты в форме модели в моем приложении Rails:

<%= f.date_select :birthday,
  {:start_year => Time.now.year,
  :end_year => 1900,
  :use_short_month => true,
  :order => [:month, :day, :year],
  :prompt => {:month => 'Month', :day => 'Day', :year => 'Year'}},
  {:class => 'year',
  :id => 'user_birthday'}
%>

Оно проверяется в коде модели с помощью:

validates_presence_of :birthday, :message => 'is a required field'

К сожалению, еслиПользователь вводит частичное значение, такое как просто год, форма по-прежнему отправляется без ошибок.Вместо этого в БД записывается значение в стиле фанк.Как сделать все три поля обязательными?

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

Спасибо!Моу

Ответы [ 4 ]

2 голосов
/ 07 марта 2011

Я думаю, вам нужно будет создать проверку в самом контроллере.

Части даты передаются в birthday(1i), birthday(2i) и birthday(3i).Проблема здесь в том, что они назначаются немедленно при передаче атрибутов и, следовательно, до того, как произойдут какие-либо проверки.

Вы также можете перезаписать метод attributes=, чтобы создать там свою собственную проверку, но я бы не советовал вам делать это.это.

Имейте в виду, что, если вы проводите валидацию, было бы неплохо проверить и на любую неправильную дату.(например, 31 февраля, который при прохождении даст 2 марта, а не ошибку).

Я думаю, что главная проблема здесь в том, что ActiveRecord фактически заменяет пустые значения на 1 перед созданием Date, что также означает, что если посетитель пройдет только год, дата будет создана 1 января этого года.Я предполагаю, что это ожидаемое поведение, позволяющее использовать только один из выбора года / месяца / дня и по-прежнему создавать полезную дату.

1 голос
/ 08 марта 2011

В связи с этим постом , это лучшее решение, которое я нашел. Однако я должен добавить: день,: месяц,: год как attr_accessible, но я не понимаю, почему .. (из-за проверки? Пожалуйста, дайте мне знать)рб

MONTHS = ["January", 1], ["February", 2], ...
DAYS = ["01", 1], ["02", 2], ["03", 3], ...
START_YEAR = Time.now.year - 100
END_YEAR = Time.now.year
YEAR_RANGE = START_YEAR..END_YEAR

attr_accessible :day, :month, :year
attr_accessor :day, :month, :year

before_save :prepare_birthday

validate :validate_birthday

private

def prepare_birthday
  begin
    unless year.blank? # in order to avoid Year like 0000
      self.birthday = Date.new(self.year.to_i, self.month.to_i, self.day.to_i)
    end
      rescue ArgumentError
    false
  end
end

def validate_birthday
  errors.add(:birthday, "Birthday is invalid") unless prepare_birthday
end

форма регистрации пользователя

<%= f.select :month, options_for_select(User::MONTHS), :include_blank => "Month" %>
<%= f.select :day, options_for_select(User::DAYS), :include_blank => "Day" %>
<%= f.select :year, options_for_select(User::YEAR_RANGE), :include_blank =>"Year" %>
0 голосов
/ 12 августа 2011

После следования советам Бенуа я придумал нечто подобное, используя виртуальные атрибуты.На стороне просмотра есть 3 отдельных элемента select (year, mon, day) внутри поля fields_for.Данные передаются в массовое назначение контроллера (без изменений в контроллере, см. asciicasts # 16 ) и затем передаются получателю / установщику (то есть виртуальному атрибуту) в модели.Я использую Rails 3.0.3 и simpleForm для кода представления.

В представлении:

<%= f.label "Design Date", :class=>"float_left" %>
<%= f.input :design_month, :label => false, :collection => 1..12 %>
<%= f.input :design_day,   :label => false, :collection => 1..31 %>
<%= f.input :design_year,  :label => false, :collection => 1900..2020 %>

В модели:

validate :design_date_validator 

def design_year
  design_date.year
end
def design_month
  design_date.month
end
def design_day
  design_date.day
end
def design_year=(year)
  if year.to_s.blank?
    @design_date_errors = true  
  else      
    self.design_date = Date.new(year.to_i,design_date.month,design_date.day)
  end
end  
def design_month=(month)
  if month.to_s.blank?
     @design_date_errors = true  
   else      
   self.design_date = Date.new(design_date.year,month.to_i,design_date.day)
  end
end  
def design_day=(day)
  if day.to_s.blank?
    @design_date_errors = true  
  else      
   self.design_date = Date.new(design_date.year,design_date.month,day.to_i)
  end
end  
#validator
def design_date_validator
  if @design_date_errors
    errors.add(:base, "Design Date Is invalid")
  end
end  

'design_date_attr' - это виртуальный атрибут, который устанавливает значение design_date в базе данных.Получатель передает обратно хэш, аналогичный тому, который передается в форме.Сеттер проверяет наличие пробелов, создает новый объект даты и устанавливает его, а также устанавливает переменную ошибки.Пользовательский валидатор: design_date_validator проверяет переменную экземпляра ошибки и устанавливает переменную ошибок.Я использовал ': base', потому что имя переменной было нечитаемым, а использование base удаляет это значение из строки ошибки.

Несколько вещей, которые необходимо изменить для рефакторинга, могут быть переменной экземпляра проверки ошибок, но, по-видимому, она работает по крайней мере.Если кто-нибудь знает лучший способ обновить объекты Date, я бы хотел это услышать.

0 голосов
/ 21 июля 2010

Вы можете переопределить метод validate_on_create, например:

class MyModel < ActiveRecord::Base
  def validate_on_create
    Date.parse(birthday)
  rescue
    errors.add_to_base("Wrong date format")
  end
end
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...