将多个字段Rails转换为一个模型属性

时间:2011-10-11 10:58:31

标签: ruby-on-rails activerecord

我一直在寻找各处,但似乎无法为此找到一个好的解决方案。

我的表单有一个日期(带有datepicker的文本字段)和一个时间(带有timepicker的文本字段),我想将其映射到名为due_at的模型字段。

到目前为止,我一直在我的控制器中处理它,使用单独的参数将其连接到日期时间,然后手动设置模型字段,但它很混乱,并且认为这个逻辑应该保存在模型/视图中。

我希望能够将两个表单字段处理为模型中的属性,然后将其拆分为错误,编辑操作等。基本上是执行标准 datetime_select 确实如此,但我要自己动手。

有什么东西可以放在我的模型中吗?

def due_at=(date, time)
...
end

我一直在寻找一些地方,但无法知道你将如何做到这一点。人们说使用javascript来填充一个隐藏的字段,但对于一个非常简单的问题,这似乎不是最干净的解决方案。

非常感谢任何建议/帮助。

感谢。

2 个答案:

答案 0 :(得分:7)

首先:请重命名您的字段,因为created_at可能会导致与ActiveRecord冲突。

我为格式为M / D / YYYY H:M(24小时格式的小时/分钟)的字段做了这个

在你的模特中:

attr_accessor :due_date, :due_time

before_validation :make_due_at


def make_due_at
  if @due_date.present? && @due_time.present?
    self.due_at = DateTime.new(@due_date.year, @due_date.month, @due_date.day, @due_time.hour, @due_time.min)
  end
end


def due_date
  return @due_date if @due_date.present?
  return @due_at if @due_at.present?
  return Date.today
end

def due_time
  return @due_time if @due_time.present?
  return @due_at if @due_at.present?
  return Time.now
end 


def due_date=(new_date)
  @due_date = self.string_to_datetime(new_date, I18n.t('date.formats.default'))
end


def due_time=(new_time)
  @due_time = self.string_to_datetime(new_time, I18n.t('time.formats.time'))
end

protected

def string_to_datetime(value, format)
  return value unless value.is_a?(String)

  begin
    DateTime.strptime(value, format)
  rescue ArgumentError
    nil
  end
end

现在在视图中:

<%= text_field_tag :due_time, I18n.l(@mymodel.due_time, :format => :time) %>
<%= text_field_tag :due_date, I18n.l(@mymodel.due_date, :format => :default) %>

现在在config / locales / en.yml(如果是英文)

  date:
    formats:
      default: "%m/%d/%Y"
  time:
    formats:
      time: "%H:%M"

您可以更改当然的日期格式。

答案 1 :(得分:1)

我认为date_time_attribute gem对你来说是个不错的选择:

class Task < ActiveRecord::Base
  include DateTimeAttribute

  date_time_attribute :due_at
end

它允许您分别设置due_at_datedue_at_time

form_for @event do |f|
  f.text_field :due_at_date
  f.text_field :due_at_time
  f.submit
end

# this will work too:
form_for @event do |f|
  f.date_select :due_at_date
  f.time_select :due_at_time, :ignore_date => true
  f.text_field  :due_at_time_zone
  f.submit
end

# or
form_for @event do |f|
  f.date_select :due_at_date
  f.text_field  :due_at_time
  f.submit
end

以下是一个例子:

task = Task.new
task.due_at                      # => nil

# Date set
task.due_at_date = '2001-02-03'
task.due_at_date                 # => 2001-02-03 00:00:00 +0700
task.due_at_time                 # => 2001-02-03 00:00:00 +0700
task.due_at                      # => 2001-02-03 00:00:00 +0700

# Time set
task.due_at_time = '10:00pm'
task.due_at_time                 # => 2013-12-02 22:00:00 +0800
task.due_at_date                 # => 2001-02-03 00:00:00 +0700
task.due_at                      # => 2001-02-03 22:00:00 +0700

# Time zone is applied as set
task.due_at_time_zone = 'Moscow'
task.due_at                      # => Mon, 03 Feb 2013 22:00:00 MSK +04:00
task.due_at_time_zone = 'London'
task.due_at                      # => Mon, 03 Feb 2013 22:00:00 GMT +00:00

它还允许您使用时区,使用慢性等。