开发者

Errors don't prevent object from saving?

开发者 https://www.devze.com 2023-03-31 10:32 出处:网络
I have a virtual attribute that takes a time range from a form field and splits it: def time_range=(time_range)

I have a virtual attribute that takes a time range from a form field and splits it:

def time_range=(time_range)
  unless time_range.empty?
    t = time_range.split(/to|\-/)
    self.start_entry = t[0]
    self.finish_entry = t[1]
    if Chronic.parse(self.start_entry).nil? || Chronic.parse(self.finish_entry).nil?
      errors.add(:time_range, 'Invalid time range entered')
    end
  end
end

start_entry and finish_entry are also virtual attributes as I have other ways to set them. Regardless of how the two were set, I have the following hook to set start and finish in my database:

before_save :set_start_and_finish

Despite the fact that I add an error, erroneous obj开发者_运维知识库ects still manage to save:

> t = Tour.new
> t.time_range = "rubbish"
> t.errors
#=> {:time_range=>["Invalid time range entered"]}
> t.valid?
#=> true

How can I invalidate the instance to prevent it from saving later?


Calling t.valid? will clear the errors before running the validations so your validations inside time_range= are ignored.

If we look at ActiveRecords's valid?, we see this:

def valid?(context = nil)
  context ||= (new_record? ? :create : :update)
  output = super(context)
  #...

And the super should send you into ActiveModel's valid? which starts like this:

def valid?(context = nil)
  current_context, self.validation_context = validation_context, context
  errors.clear
  #...

and the clear call nukes the errors you added in time_range=.

If you want to validate something, use the validators. If you want to prevent an invalid assignment, raise an ArgumentError (or other more appropriate exception).

Having the validation system reset itself (i.e. errors.clear) before running the validations does make sense. If it didn't reset, you'd have to throw away and reload an invalid object (or manually reset it) just to correct a validation error. Just because "update, validate, save or destroy" is the general workflow for a web-app doesn't mean that it is the only possible workflow for a database-backed application.


set_start_and_finish seems like an odd place to be checking for validation errors, but make sure that you are returning false if you detect an error to stop the rest of the callbacks from executing.

Read the section on "Cancelling callbacks"


Try calling set_start_and_finish on validate instead of before_save
As validate :set_start_and_finish

0

精彩评论

暂无评论...
验证码 换一张
取 消