Rails 3 SQLite3 Boolean false
我试图在SQLite3表中插入一个错误的布尔值,但它总是插入一个真值。
这是我的迁移:
class CreateUsers false, :null => false end end def self.down drop_table :resources end end
当我尝试使用rails插入时,它会生成以下SQL:
INSERT INTO "users" ("name", "active") VALUES ('test', 'f')
SQLite将’f’视为true,因此它将true插入到我的数据库中。 我想要它生成的查询是:
INSERT INTO "users" ("name", "active") VALUES ('test', false)
我究竟做错了什么?
rails:3.0.7
sqlite3 gem:1.3.3
SQLite使用1表示true,0表示false表示 :
SQLite没有单独的布尔存储类。 相反,布尔值存储为整数0(假)和1(真)。
但SQLite也有一个松散的类型系统并自动转换东西,所以你的'f'
可能被解释为具有真实性,因为它不是零。
一点挖掘表明您在Rails 3.0.7 SQLiteAdapter中发现了一个错误。 在active_record/connection_adapters/abstract/quoting.rb
,我们发现这些:
def quoted_true "'t'" end def quoted_false "'f'" end
因此,默认情况下,ActiveRecord假定数据库理解布尔列的't'
和'f'
。 MySQL适配器会覆盖它们以使用tinyint
列的tinyint
实现:
QUOTED_TRUE, QUOTED_FALSE = '1'.freeze, '0'.freeze #... def quoted_true QUOTED_TRUE end def quoted_false QUOTED_FALSE end
但SQLite适配器不提供自己的quoted_true
或quoted_false
实现,因此它获得了不适用于SQLite的布尔值的默认值。
't'
和'f'
布尔值在PostgreSQL中工作,所以也许每个人都使用PostgreSQL和Rails 3,或者他们只是没有注意到他们的查询不能正常工作。
我对此感到有些惊讶,希望有人可以指出我出错的地方,你不能成为第一个在SQLite中使用Rails 3的布尔列的人。
尝试猴子修补def quoted_true;'1';end
和def quoted_false;'0';end
到ActiveRecord::ConnectionAdapters::SQLiteAdapter
(或暂时手动编辑它们到active_record/connection_adapters/sqlite_adapter.rb
),看看你是否明智SQL。
我也碰到了这个,这里是如何修补补丁:
require 'active_record/connection_adapters/sqlite_adapter' module ActiveRecord module ConnectionAdapters class SQLite3Adapter < SQLiteAdapter def quoted_true; '1' end def quoted_false; '0' end end end end
我不知道我是如何运行这个错误的?
您可能会发现以下代码片段用于添加与实际使用Rails 4的SQLite布尔列的兼容性(也发布在https://gist.github.com/ajoman/9391708 ):
# config/initializers/sqlite3_adapter_patch.rb module ActiveRecord module ConnectionAdapters class SQLite3Adapter < AbstractAdapter QUOTED_TRUE, QUOTED_FALSE = "'t'", "'f'" def quoted_true QUOTED_TRUE end def quoted_false QUOTED_FALSE end end end end
这是在2017年7月12日在master上修复的 。但它不是最新稳定版本(5.1.4)的一部分 。 它修复的最新版本是v5.2.0.rc1 。
可以通过Rails.application.config.active_record.sqlite3.represent_boolean_as_integer
设置行为( 默认为true
)。
此版本适用于Rails 4.1。
require 'active_record/connection_adapters/sqlite_adapter' module ActiveRecord::ConnectionAdapters::SQLite3Adapter QUOTED_TRUE, QUOTED_FALSE = 't'.freeze, 'f'.freeze def quoted_true; QUOTED_TRUE end def quoted_false; QUOTED_FALSE end end
常量和.freeze
用于提高性能,因此ruby不必重新生成这些字符串,垃圾会在每次调用时收集它们。