为什么Rails在创建时将我的属性设置为nil?

时间:2015-08-11 19:41:06

标签: ruby-on-rails ruby validation ruby-on-rails-4 activerecord

Ruby 2.2.0 on Rails 4.2.2

我正在尝试编写一个自定义日期验证器(一旦我使这部分工作,它将被扩展以处理其他一些情况),但是现在它无法对字符串进行适当的验证(并返回false) - 它甚至没有跑。当设置为字符串时,rails似乎完全忽略date_ended值。当我尝试相同的测试,除了一个整数,它正确验证并验证失败。如果我允许nil值,验证器会正确地阻止在字符串值上创建记录,但这只是因为它拒绝了nil值。任何和所有建议都表示赞赏。

date_started存在同样的问题。

编辑:我已经确认第二次预期验证失败,第一次验证是通过在第一次调查之前仔细检查CommissionMember.count是否为0期望。

CommitteeMember

class CommitteeMember < ActiveRecord::Base
  belongs_to :committee
  belongs_to :member

  validates :committee_id, presence: true
  validates :member_id, uniqueness: { scope: :committee_id }, presence: true
  validates :date_started, date: true, allow_nil: true
  validates :date_ended, date: true, allow_nil: true
end

schema.rb 相关行:

create_table "committee_members", force: :cascade do |t|                                          
  t.integer  "committee_id", null: false                                                          
  t.integer  "member_id",    null: false                                                          
  t.date     "date_started"                                                                       
  t.date     "date_ended"                                                                         
  t.datetime "created_at",   null: false                                                          
  t.datetime "updated_at",   null: false                                                                                                                                                                                                                          
end 

DateValidator (自定义验证程序):

(请注意中间的打印值)

class DateValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    puts "Validating #{value}"
    unless value.kind_of?(Date)
      record.errors[attribute] << "must be of type date"
    end
  end
end

CommiteeMember 相关规范:

(请注意中间的打印值)

it 'should fail with a string for date_ended' do
  expect(CommitteeMember.count).to eq(0)
  CommitteeMember.create!(member_id: 1, committee_id: 1, date_ended: "S")
  ap CommitteeMember.first
  expect(CommitteeMember.count).to eq(0)
end

规范输出:

$ rspec spec/models/committee_member_spec.rb
......#<CommitteeMember:0x00000007d1f888> {
              :id => 1,
    :committee_id => 1,
       :member_id => 1,
    :date_started => nil,
      :date_ended => nil,
      :created_at => Tue, 11 Aug 2015 19:22:51 UTC +00:00,
      :updated_at => Tue, 11 Aug 2015 19:22:51 UTC +00:00
}
F

Failures:

  1) CommitteeMember validations should fail with a string for date_ended
     Failure/Error: expect(CommitteeMember.count).to eq(0)

       expected: 0
            got: 1

       (compared using ==)
     # ./spec/models/committee_member_spec.rb:52:in `block (3 levels) in <top (required)>'

Finished in 0.54864 seconds (files took 2.43 seconds to load)
7 examples, 1 failure

Failed examples:

rspec ./spec/models/committee_member_spec.rb:48 # CommitteeMember validations should fail with a string for date_ended

1 个答案:

答案 0 :(得分:3)

由于属性是日期属性,因此Rails会自动解析您尝试分配的任何值。如果它无法解析为日期,则会将值保留为nil,例如在控制台中:

c = CommitteeMember.new
c.date_started = 'S'
=> "S"
c.date_started
=> nil

实际上,Rails实际上会将字符串解析为Date:

c.date_started = '2016-1-1'
=> "2016-1-1"
c.date_started
=> Fri, 01 Jan 2016
c.date_started.class
=> Date

这意味着您根本不需要验证日期字段是否为日期,因为Rails不会以其他方式存储它们。相反,只需验证它们是否存在:

validates_presence_of :date_started, :date_ended