Rails 3 SQLite3 布尔值 false
Posted
技术标签:
【中文标题】Rails 3 SQLite3 布尔值 false【英文标题】:Rails 3 SQLite3 Boolean false 【发布时间】:2011-08-26 04:02:05 【问题描述】:我试图在 SQLite3 表中插入一个假布尔值,但它总是插入一个真值。
这是我的迁移:
class CreateUsers < ActiveRecord::Migration
def self.up
create_table :users do |t|
t.column :name, :string
t.column :active, :boolean, :default => 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)
我做错了什么?
导轨:3.0.7
sqlite3 宝石:1.3.3
【问题讨论】:
你的 Rails 代码用 'f' 生成 SQL 是什么? 生成的 html 如下所示: 控制器是一个简单的 REST 创建。 即使我执行如下查询:User.find(:all, :conditions => :active => false) sql 看起来像这样:SELECT "users".* FROM "users " WHERE "用户"."active" = 'f' 【参考方案1】:SQLite 使用1 for true and 0 for 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
实现的布尔列:
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 中工作,所以也许每个人都在使用带有 Rails 3 的 PostgreSQL,或者他们只是没有注意到他们的查询不能正常工作。
对此我有点惊讶,希望有人能指出我哪里出错了,你不可能是第一个在 Rails 3 中使用 SQLite 布尔列的人。
尝试将 def quoted_true;'1';end
和 def quoted_false;'0';end
修改为 ActiveRecord::ConnectionAdapters::SQLiteAdapter
(或暂时将它们手动编辑为 active_record/connection_adapters/sqlite_adapter.rb
),看看您是否获得了合理的 SQL。
【讨论】:
是的,我知道这一点。我只是不知道为什么它使用 'f' 而不是 false 或 0,因为 0 在 POST 中传递。 @Mike:请看一下我的更新。我删除了我原来的答案,然后将其复活,并根据对 ActiveRecord 数据库适配器源代码的快速回顾进行了更新。 这就是我的想法...我不能成为第一个遇到这个错误的人,这让我认为这与我的环境有关。我会稍等一下,看看其他人是否有其他解释,否则我会给你功劳。 原来 sqlite 适配器没有问题。这是我的数据库查看软件,其中有错误。非常感谢您的深入回答,我希望我能给您功劳。 @Mike:Rails 在 SQLite 数据库中使用't'
和 'f'
作为布尔值,对吧?只要只有 Rails 使用数据库就可以正常工作,SQLite 在内部将所有内容都视为字符串,因此 DDL 中的列类型主要是建议性的。我只是好奇我是否正确阅读了源代码,也许我会设置自己的 Rails/SQLite 测试应用程序看看。【参考方案2】:
我也遇到过,这里是猴子补丁的方法:
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
我不明白我是怎么遇到这个错误的??
【讨论】:
使用 't' 和 'f' 而不是 '1' 和 '0'。这应该是评论,而不是答案。 这不适用于 Rails 5。你有更新版本吗?【参考方案3】:您可能会发现以下代码 sn-p 对于添加与实际在 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
【讨论】:
这不适用于 Rails 5。你有更新版本吗? @thisismydesign 抱歉,我没有更新版本,因为我不再使用 SQLite 在 Rails 上进行开发。【参考方案4】:这是 2017 年 7 月 12 日在 master 上的 fixed。但它是 not part of the latest stable release (5.1.4)。修复它的最新版本是v5.2.0.rc1。
可以通过Rails.application.config.active_record.sqlite3.represent_boolean_as_integer
(default is true
) 设置行为。
【讨论】:
【参考方案5】:此版本适用于 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 不必在每次调用时重新生成这些字符串并垃圾收集它们。
【讨论】:
以上是关于Rails 3 SQLite3 布尔值 false的主要内容,如果未能解决你的问题,请参考以下文章
Rails4迁移 - 向add_reference方法添加null:false?
在Flask-SQLAlchemy中使用SQLite3时模拟布尔值的ARRAY