【问题标题】:How are values cast when assigning to a Boolean field in Rails ActiveRecord?分配给 Rails ActiveRecord 中的布尔字段时如何转换值?
【发布时间】:2014-03-13 08:09:19
【问题描述】:

我的问题的简短版本

在 Rails ActiveRecord 中,如果我有一个布尔字段并将其分配为“abc”或2,那么它会立即转换为false。值1 被转换为true,而nil 保持为nil。为什么会这样?在哪里可以找到解释此行为的 Rails 文档(或 Ruby 文档)?

我的问题的长版本

我很难理解 Rails 如何处理将值分配给 Rails 模型中的 Boolean 字段的尝试。例如,假设我有一个 Website 模型,其中有一个名为 :domainString 字段和一个名为 :can_sslBoolean 字段。

我的迁移如下所示:

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

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

class Website < ActiveRecord::Base
  validates :domain, :presence => true
  validates :can_ssl, :inclusion =>  { :in => [true, false] }
end

足够简单。根据我所做的,我期望 :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

所以,根据我目前所做的,我认为我可以得出以下结论:

  • 将值1true 分配给Boolean 字段时,该值将立即转换为true,然后进行分配。
  • nil 分配给Boolean 字段时,该字段实际上是分配了nil
  • 当分配其他任何东西(例如String 或任何不是1 的数字)时,该值将立即转换为false

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

我很难在 Rails 中找到 Boolean 字段类型的文档,这可以让我澄清这一点。

【问题讨论】:

    标签: ruby-on-rails activerecord boolean


    【解决方案1】:

    这是在 ActiveRecord 的内部完成的:特别是

    ActiveRecord::ConnectionAdapters::Column.value_to_boolean
    

    至少在我的 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
    

    其中 TRUE_VALUES 定义为

    #activerecord/lib/active_record/connection_adapters/abstract/schema_definitions.rb:10:      
    TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE'].to_set
    

    1、0、“f”和“t”之所以存在,是因为 MySql 和 PostgreSql 等流行的 DBMS 将布尔值分别存储为 0/1 和“f”/“t”。

    值得注意的是这与在 Ruby/Rails 中通过“if”测试的值之间的区别,即“真”或“假”值(“假”值将通过 if 测试,“真”值将通过它)。

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

    【讨论】:

      【解决方案2】:

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

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

      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
      

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

      [false, 0, '0', 'f', 'F', 'false', 'FALSE', 'off', 'OFF'].to_set
      

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

      此更改将在 Rails 5.0 上生效。

      【讨论】:

        【解决方案3】:

        这是 Ruby 的东西,而不是 Rails 的东西。布尔值是“真实的”。这里有一篇非常有用的文章。 https://gist.github.com/jfarmer/2647362

        【讨论】:

        • 这不是他要问的:他问的是“为什么 'abc' 被保存为布尔列的“假””。 "abc" 在 ruby​​ 中是真实的,这似乎是一个矛盾。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2013-07-16
        • 2020-01-10
        • 1970-01-01
        • 2011-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多