使用虚拟属性时如何添加错误消息

How to Add an Error Message when using a Virtual Attribute

我有一个日期字段不是必填字段。我正在使用 Chronic 将用户输入字符串格式化为日期字段的有效 rails 格式。如果 Chronic 无法解析日期,我想提出一个错误,使用相应的错误消息和原始输入值呈现编辑视图。目前,如果输入无效日期,则更新成功,但 service_date 字段没有任何更新。

new.html.erb

<%= f.text_field :service_date_text %>

bill.rb

require 'chronic'

class Bill < ActiveRecord::Base

  def service_date_text
    service_date.try(:strftime, "%m/%d/%Y")
  end

  def service_date_text=(date)
    if date.present?
      if Chronic.parse(date)
        self.service_date = Chronic.parse(date)
      else
        self.errors.add(:service_date_text, "invalid date format hello.")
      end
    else
      self.service_date = ''
    end
  end
end

bills_controller.rb

  def update
    @bill = current_account.bills.find(params[:id])
    if @bill.update_attributes(bill_params)
      redirect_to @bill, notice: 'Bill has been successfully updated.'
    else
      render :edit
    end
  end

  private

  def bill_params
    params.require(:bill).permit(:description, :notes, :po_number, :service_date_text)
  end

我敢打赌问题是 Rails 不希望 setter 方法添加错误。我会让 service_date_text 只是一个 attr_accessor,然后调用一个验证方法来设置 service_date 或添加一个错误。

attr_accessor :service_date_text

validate :service_date_text_format

private

def service_date_text_format
  return unless service_date_text # or self.service_date ||= '' and then return
  if date = Chronic.parse(date)
    self.service_date = date
  else
    errors.add(:service_date, 'invalid format')
  end
end

errors 会在您 运行 valid? 时被清除,而 update_attributes 会。

示例:

irb(main):001:0> album = Album.new
=> #<Album id: nil, name: nil, release_date: nil, rating: nil, genre_id: nil,
artist_id: nil, created_at: nil, updated_at: nil>

irb(main):004:0> album.errors.add :artist, "You've selected Justin Bieber (!!!)"
=> ["You've selected Justin Bieber (!!!)"]

irb(main):006:0> album.errors.messages
=> {:artist=>["You've selected Justin Bieber (!!!)"]}

irb(main):007:0> album.valid?
=> true

irb(main):008:0> album.errors.messages
=> {}

不要滥用设置器,使用适当的验证。例如(未测试):

  require 'chronic'

  class Bill < ActiveRecord::Base
    validate :service_date_validation

    def service_date_text
      service_date.try(:strftime, "%m/%d/%Y")
    end

    def service_date_text=(date)
      if date.present?
        if Chronic.parse(date)
          self.service_date = Chronic.parse(date)
        else
          self.service_date = false
        end
      else
        self.service_date = ''
      end
    end


    private

      def service_date_validation
        if self.service_date == false
          self.errors.add(:service_date_text, "invalid date format hello.")
        end
      end
  end

... 还有一些提供日期验证的 gem,例如:

...以及其他一些...