【问题标题】:Why is Rails setting my attribute to nil on create?为什么 Rails 在创建时将我的属性设置为 nil?
【发布时间】:2015-08-11 19:41:06
【问题描述】:

Ruby 2.2.0 on Rails 4.2.2

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

date_started 也存在同样的问题。

编辑:我已经确认第二个期望的验证失败了,不是通过在第一个验证之前再次检查 CommitteeMember.count 是否为 0期待。

委员会成员

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

【问题讨论】:

  • date_starteddate_ended 是什么类型的列?日期、日期时间或其他?如果不确定,请查看您的 db/schema.rb
  • 天哪。对不起,我错过了!两种日期类型。我会更新问题。

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


【解决方案1】:

由于属性是日期属性,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

【讨论】:

  • 这就解释了!谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-03-18
  • 2023-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多