【问题标题】:Rails ActiveRecord callbacksRails ActiveRecord 回调
【发布时间】:2014-01-24 23:40:10
【问题描述】:

我遇到了日期格式问题。我有一个时间选择器,它的日期格式很时髦(嗯,实际上这是一种很好的格式,但对计算机来说不是)。我正在尝试让Chronic 解析日期以便正确保存。

起初,我在控制器的创建动作中做:

params[:event][:start] = Chronic.parse(params[:event][:start])

但是,如果验证失败,它会将解析后的值发送回视图,然后我的 datetimepicker 全部搞砸了。

所以,我想……回调?在我的模型中,我添加了:

private
  def date_change
    self.start = Chronic.parse(self.start)
  end

我尝试了before_savebefore_validationafter_validation...,但似乎没有正确格式化该日期。

就目前而言,我不断收到ArgumentError in EventsController#create - Argument out of range。我认为这是因为数据库需要一个格式正确的日期时间对象。

知道如何实现我的目标,即不更改params,但仍然能够保存格式正确的对象吗?

【问题讨论】:

标签: ruby-on-rails ruby activerecord


【解决方案1】:

我猜测问题出在 ActiveRecord 提供的start= mutator 方法上。如果你在你的控制器中做这样的事情:

@event.update_attributes(params[:events])
@event = Event.create(params[:event])
#...

那么createupdate_attributes 应该在内部调用start=。这应该可以让你把慢性病的东西放在你自己的start=

def start=(t)
  super(Chronic.parse(t))
end

您可能需要针对非字符串 ts 进行调整,例如,我不确定 Chronic.parse(Time.now) 会做什么。如果您不想踢到super,也可以致电write_attribute(:start, Chronic.parse(t))self[:start] = Chronic.parse(t)

请注意,before_validation 和类似的处理程序将被调用太晚,无法绕过 ActiveRecord 正在执行的任何默认字符串到时间戳转换,但应该在正确的时间发生突变器覆盖。

或者,您可以使用以下方式解析控制器中的时间:

event = params[:events].dup
events[:start] = Chronic.parse(events[:start])
@event = Event.create(event)

【讨论】:

  • 所以,基本上,只需重写一个 setter 方法。我喜欢。我刚刚尝试过,如果验证失败,它仍然会将解析的日期时间发送回视图。这反过来又破坏了我的日期选择器......
  • 听起来你有一个视图问题,视图应该将时间转换为你的 JS 需要的任何格式。我仍然认为您的选择器应该发送和接收 ISO 8601 格式。
  • 不...一旦@event 尝试构建(通过修改参数,即使在复制它们之后),它会将解析的信息反馈回视图。如果您想查看所有代码,我不介意 - github.com/Mallanaga/palv2
  • 您的视图可以做的不仅仅是<%= @event.start %>,如果您的小部件需要特定格式的@event.start,那么请在视图中进行翻译。或者修复小部件,使其在接受日期时更加灵活。
  • 它是表单的一部分。 = f.text_field :start, class: 'form-control', placeholder: 'start' 我知道操纵日期/时间的唯一方法是使用.strftime,我不知道如何以这样的形式使用它。不幸的是,我对小部件的了解不够,无法“修复”它。 =/
【解决方案2】:

假设是所有混乱之母 :)

  • 你确定回调被命中了吗?因为如果它会,并且发生错误(就像它那样),它不会仍然将不正确的数据(因为已解析)发送回视图吗?如有疑问:记录一些内容以确保它被命中。
  • 您确定是哪个字段导致了Argument out of range 错误。

大多数情况下的错误很难找到/修复,因为我们假设我们知道错误,但我们正在以错误的方式查看错误。

测试哪个属性导致错误的简单方法:

  • 打开rails console,用参数构建一个对象,保存它,然后询问错误。类似的东西

    e = Event.new(params[:event]) # 从日志文件中逐字复制参数 e.保存 e.错误

这将显示导致错误的字段。

或者:使用 pry 并在保存后添加一行 binding.pry,以便检查 errors(更多 info

回答(假设你的假设是正确的)

我看到两个选项可以做你想做的事:

  • 使用after_validation 回调,如果您确定数据将始终正确,并由Chronic 正确解析。这样,如果验证通过,则转换字段,通常不会再出错,并且该值永远不会再次发送到浏览器。

注意:如果是其他属性导致错误,当然不会触发此回调。因为它没有通过验证。

  • 使用虚拟属性,例如start_str,这是您开始的可视化表示,以及 before_save 将其转换为 start。这并不重要,因为如果验证失败,您只会显示 start_str 而不是“真实的”start 字段。

【讨论】:

  • 我听到你的假设......我知道这是日期,因为我用不同的方法工作,但想改变它。我在控制台中对其进行了测试。使用start: Date.now 而不是start: '01/24/2014 4:29 PM' 构建一个事件,一切都很好。我尝试使用after_validation,虽然......没有骰子。我知道数据永远是正确的,所以我并不担心。不过,它似乎没有达到 after_validation 方法......
  • 事实上...我只是尝试仅使用日期构建(在应用程序中),没有其他任何内容,并且验证不会失败 - 它直接进入 argument out of range 错误。似乎模型甚至不会使用不兼容的数据构建?我不想使用虚拟属性——它们的验证效果不好。
  • 控制器上的哪一行是?你能给我们看一个堆栈跟踪吗?
  • 添加虚拟属性非常简单:只需attr_accessor start_str。我没有得到关于验证的评论?如果您需要对其进行验证,则需要添加自己的验证,但您也需要在此处添加特定的转换。您还可以查看 Rails 默认日期格式。我可以为我的日期字段分配一个字符串,rails 会自动转换它,除非它是一种奇怪的格式。但是超出范围的错误听起来像别的东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多