Как Ruby on rails многопараметрические атрибуты * действительно * работают (datetime_select)
Я считаю, что datetime_select
- черная магия. То, что я действительно пытаюсь понять, это целые 1i
, 2i
, 3i
, 4i
... несколько параметров. В частности, как это обрабатывается в задней части (activerecord, что-то еще?). Что с "i" после номера заказа? Это спецификатор типа? Если да, то какие другие типы доступны? Я прочитал источник date_helper.rb, и он довольно непрозрачен.
Здесь моя мотивация:
У меня есть столбец :datetime
в моей модели, и я хочу ввести в представление через два text_field
s: один для даты и один для времени. Они должны быть проверены, объединены вместе, а затем сохранены в столбце datetime. В конечном счете, я буду использовать календарь javascript для ввода дат в поле даты.
Так кто-нибудь это сделал? Я пробовал использовать виртуальные атрибуты (невероятно недокументированные, кроме рудиментарного railscast), и проблема заключалась в том, что когда создается новый объект activerecord и имеет атрибуты nil, виртуальный атрибут терпит неудачу (undefined метод strftime
для класса nil, что имеет смысл).
У кого-нибудь есть предложения или рекомендации? Спасибо!
Ответы
Ответ 1
У меня был тот же вопрос. Вот что я нашел...
http://apidock.com/rails/ActiveRecord/Base/assign_multiparameter_attributes
assign_multiparameter_attributes (пар) частный
Создает объекты для всех атрибутов классы, которым требуется больше одного конструктор. Это делается путем вызов new для типа столбца или тип агрегации (через составленный) объект с этими параметрами. Так с парами, записанными в (1) = "2004", written_on (2) = "6", written_on (3) = "24", будет создавать экземпляр write_on (тип даты) с Date.new( "2004", "6", "24" ). Ты можешь также укажите тип-тип в скобки должны иметь параметры Привилегированные до их использования в конструктор. Используйте я для Fixnum, f для Float, s для String и a для массива. Если все значения для данного атрибут пуст, атрибут будет установлено значение nil.
Таким образом, цифры относятся к параметрам типа данных, которые относятся к столбцу. "I" должен преобразовать его в целое. Другие типы поддерживаются как "f" для float и т.д.
Здесь вы можете увидеть execute_callstack_for_multiparameter_attributes, что он обнаруживает целевой тип и передает его значения.
Итак, чтобы напрямую ответить на поставленный вопрос, я не думаю, что вы можете использовать его, чтобы переопределить, как создается DateTime, потому что он использует конструктор, который не поддерживает формат, который вы хотите передать. вокруг было разделение столбца DateTime на один столбец "Дата" и "Один раз". Тогда пользовательский интерфейс мог работать так, как я хотел.
Интересно, можно ли создавать атрибутные атрибуты класса, которые представляют дату и время отдельно, но сохранить один столбец DateTime. Затем форма может ссылаться на эти отдельные аксессоры. Это может быть осуществимо. Другим вариантом может быть использование composed_of для дальнейшей настройки типа.
Надеюсь, что это поможет кому-то другому.:)
Ответ 2
Вот что я в итоге придумал. Если у кого есть какие-либо комментарии, дайте мне знать. Мне бы очень хотелось получить обратную связь.
validate :datetime_format_and_existence_is_valid
before_save :merge_and_set_datetime
# virtual attributes for date and time allow strings
# representing date and time respectively to be sent
# back to the model where they are merged and parsed
# into a datetime object in activerecord
def date
if (self.datetime) then self.datetime.strftime "%Y-%m-%d"
else @date ||= (Time.now + 2.days).strftime "%Y-%m-%d" #default
end
end
def date=(date_string)
@date = date_string.strip
end
def time
if(self.datetime) then self.datetime.strftime "%l:%M %p"
else @time ||= "7:00 PM" #default
end
end
def time=(time_string)
@time = time_string.strip
end
# if parsing of the merged date and time strings is
# unsuccessful, add an error to the queue and fail
# validation with a message
def datetime_format_and_existence_is_valid
errors.add(:date, 'must be in YYYY-MM-DD format') unless
(@date =~ /\d{4}-\d\d-\d\d/) # check the date format
errors.add(:time, 'must be in HH:MM format') unless # check the time format
(@time =~ /^((0?[1-9]|1[012])(:[0-5]\d){0,2}(\ [AaPp][Mm]))$|^(([01]\d|2[0-3])(:[0-5]\d){0,2})$/)
# build the complete date + time string and parse
@datetime_str = @date + " " + @time
errors.add(:datetime, "doesn't exist") if
((DateTime.parse(@datetime_str) rescue ArgumentError) == ArgumentError)
end
# callback method takes constituent strings for date and
# time, joins them and parses them into a datetime, then
# writes this datetime to the object
private
def merge_and_set_datetime
self.datetime = DateTime.parse(@datetime_str) if errors.empty?
end