在Rails ActiveRecord中分配给布尔字段时,值如何转换?

Alv*_*Lee 9 activerecord boolean ruby-on-rails

我的问题的简短版本

在Rails ActiveRecord中,如果我有一个布尔字段并且我给它分配了类似abc"或"的东西2,那么它会立即转换为false.该值1将转换为true,并nil保持为nil.为什么会这样?我在哪里可以找到解释此行为的Rails文档(或Ruby文档)?

我的问题的长版本

我很难理解Rails如何处理Boolean为Rails模型中的字段赋值的尝试.例如,假设我有一个Website模型,其中包含一个String被调用:domainBoolean字段和一个被调用的字段:can_ssl.

我的迁移看起来像这样:

class CreateWebsites < ActiveRecord::Migration
  def change
    create_table :websites do |t|
      t.string :domain
      t.boolean :can_ssl, :default => false

      t.timestamps
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

在我的模型文件中,我添加了一些验证规则,所以它看起来像这样:

class Website < ActiveRecord::Base
  validates :domain, :presence => true
  validates :can_ssl, :inclusion =>  { :in => [true, false] }
end
Run Code Online (Sandbox Code Playgroud)

很简单.基于我做了什么,我期待:can_ssl只能设置为值truefalse,而不是其他.还有什么会导致valid?false.

但是一旦我开始在控制台中玩游戏,我注意到,早在实际的赋值语句中,我提供的值正在重铸truefalse(或nil).有关如何将值转换为什么规则Boolean

控制台的示例:

w = Website.new
w.domain = 'stackoverflow.com'
w.can_ssl = true
w.can_ssl    # => true
w.valid?     # => true

w.can_ssl = nil
w.can_ssl    # => nil
w.valid?     # => false (so far so good)

w.can_ssl = 'abc'
w.can_ssl    # => false (How did 'abc' become the value false?)
w.valid?     # => true

w.can_ssl = 1
w.can_ssl    # => true (I guess it makes sense that 1 casts to true)
w.valid?     # => true

w.can_ssl = 2
w.can_ssl    # => false (But anything other than 1 casts to false?)
w.valid?     # => true
Run Code Online (Sandbox Code Playgroud)

所以,根据我迄今所做的工作,我我可以得出以下结论:

  • 当分配值1trueBoolean现场,该值将立即得到铸成true,然后分配.
  • 分配nilBoolean字段时,实际分配字段nil.
  • 在分配任何其他内容(例如a String或任何不是的数字1)时,该值将立即转换为false.

我理解正确吗?我错过了什么吗?

我很难Boolean在Rails中找到文档字段类型,这可以让我对此做出澄清.

Max*_*ams 7

这是在ActiveRecord的内容中完成的:特别是

ActiveRecord::ConnectionAdapters::Column.value_to_boolean
Run Code Online (Sandbox Code Playgroud)

至少在我的rails版本中(在最近的版本中可能会略有不同).

这是我的版本中的来源

    # File activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb, line 144
    144:         def value_to_boolean(value)
    145:           if value.is_a?(String) && value.blank?
    146:             nil
    147:           else
    148:             TRUE_VALUES.include?(value)
    149:           end
    150:         end
Run Code Online (Sandbox Code Playgroud)

其中TRUE_VALUES定义为

#activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb:10:      
TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
Run Code Online (Sandbox Code Playgroud)

1,0,"f"和"t"是因为流行的DBMS像MySql和PostgreSql,它们分别存储为0/1和"f"/"t".

值得注意的是这与通过Ruby/Rails中的"if"测试之间的区别,即"truthy"或"falsy"的值("falsy"值将失败,如果测试,"truthy"值将通过它) .

在Ruby中,nil并且false是"虚假的",而字面上的任何其他东西(包括0,空数组,空字符串,空哈希等)都是"真实的".因此,在ruby中被认为是真实/虚假的东西与在布尔数据库列中保存为真/假的内容之间存在巨大的差异.


Tsu*_*omu 5

布尔列的行为随着两次提交而改变:

新规则非常简单。请参阅下面的代码:

module ActiveRecord
  module Type
    class Boolean < Value # :nodoc:
      def type
    :boolean
      end

      private

      def cast_value(value)
    if value == ''
      nil
    else
      !ConnectionAdapters::Column::FALSE_VALUES.include?(value)
    end
      end
    end
  end
end
Run Code Online (Sandbox Code Playgroud)

常量ConnectionAdapters::Column::FALSE_VALUES定义如下:

[false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
Run Code Online (Sandbox Code Playgroud)

如果一个值不是空字符串并且不在其中,它将被强制转换为true

此更改将在 Rails 5.0 上生效。