提供 rom-rb 属性时空验证失败

null validation failed when attribute supplied rom-rb

我正在尝试使用 sqlite3 来掌握 rom-rb 持久性库。

我 运行 以下迁移,其中包括 NOT NULL 约束:

ROM::SQL.migration do
  change do
    create_table :users do
      primary_key :id
      column :name, String, null: false
      column :age, Integer
      column :is_admin, TrueClass
    end
  end
end

这是我的简单 app.rb:

require 'rom'

rom = ROM.container(:sql, 'sqlite://db/my-db-file.db') do |config|
  class Users < ROM::Relation[:sql]
    schema(infer: true)
  end

  config.relation(:users)
end

users = rom.relations[:users]
puts users.to_a.inspect # => []

create_user = users.command(:create)
create_user.call( name: 'Rob', age: 30, is_admin: true )
puts users.to_a.inspect # never reached

尝试 运行 此脚本产生以下输出:

Roberts-MacBook-Pro:my-rom-demo Rob$ ruby app.rb 
[]
/Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/statement.rb:108:in `step': SQLite3::ConstraintException: NOT NULL constraint failed: users.name (ROM::SQL::NotNullConstraintError)
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/statement.rb:108:in `block in each'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/statement.rb:107:in `loop'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/statement.rb:107:in `each'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/database.rb:156:in `to_a'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/database.rb:156:in `block in execute'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/database.rb:95:in `prepare'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sqlite3-1.3.13/lib/sqlite3/database.rb:137:in `execute'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/adapters/sqlite.rb:189:in `block (2 levels) in _execute'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/database/logging.rb:38:in `log_connection_yield'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/adapters/sqlite.rb:189:in `block in _execute'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/database/connecting.rb:253:in `block in synchronize'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/connection_pool/threaded.rb:91:in `hold'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/database/connecting.rb:253:in `synchronize'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/adapters/sqlite.rb:180:in `_execute'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/adapters/sqlite.rb:146:in `execute_insert'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/dataset/actions.rb:1099:in `execute_insert'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/sequel-5.11.0/lib/sequel/dataset/actions.rb:399:in `insert'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-sql-2.5.0/lib/rom/sql/relation/writing.rb:39:in `insert'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-sql-2.5.0/lib/rom/sql/commands/create.rb:46:in `block in insert'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-sql-2.5.0/lib/rom/sql/commands/create.rb:46:in `map'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-sql-2.5.0/lib/rom/sql/commands/create.rb:46:in `insert'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-sql-2.5.0/lib/rom/sql/commands/create.rb:31:in `execute'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-core-4.2.1/lib/rom/command.rb:280:in `call'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-sql-2.5.0/lib/rom/sql/commands/error_wrapper.rb:16:in `call'
    from /Rob.rvm/gems/ruby-2.4.0@learn-rails/gems/rom-core-4.2.1/lib/rom/commands/composite.rb:17:in `call'
    from app.rb:15:in `<main>'

为什么它认为我的 name 属性在我提供时为空?

注意: 我在一些测试和了解 ops gem 版本后修改了我的答案

您收到 NULL CONSTRAINT 错误的原因是 ROM 没有 为用户 table.

加载架构

当您在下面定义容器时

rom = ROM.container(:sql, 'sqlite://db/my-db-file.db') do |config|
  class Users < ROM::Relation[:sql]
    schema(infer: true)
  end

  config.relation(:users)
end

你定义了两个东西,一个关系 class 绑定到一个名为 Users 的常量和一个自动生成的具有相同名称但实际上注册在 ROM 容器中的关系。实际上 Users 常量关系被忽略了。这一点很重要的原因是因为自动生成的关系不会自动从数据库中推断模式,所以当您写出数据时,模式会强制删除所有未知键,从而导致错误。您发送到数据库的所有内容是 {}.

要修复错误,只需告诉关系来推断架构 - 示例如下所示。

require 'rom'
require 'rom/sql'
require 'sqlite3'

puts "ROM Version #{ROM::Core::VERSION}" # 4.2.1
puts "ROM Version #{ROM::SQL::VERSION}" # 2.5.0
puts "Sequel Version #{Sequel::VERSION}" # 5.11.0
puts "SQLite3 Gem Version #{SQLite3::VERSION}" # 1.3.13

opts = {
  adapter: :sqlite,
  database: 'c:/mydb.db'
}

rom = ROM.container(:sql, opts) do |c|
  # Just another way to write the same users table
  # c.gateways[:default].create_table(:users) do
  #   column :id,   :integer, primary_key: true
  #   column :name, :string, null: false
  #   column :age,  :integer
  #   column :is_admin, :bool
  # end

  c.gateways[:default].create_table :users do
    primary_key :id
    column :name, String, null: false
    column :age, Integer
    column :is_admin, TrueClass
  end

  c.relation(:users) do
    schema(infer: true)
  end
end

users = rom.relations[:users]
puts users.to_a.inspect # => []

create_user = users.command(:create)
create_user.call(name: 'Rob', age: 30, is_admin: true)
puts users.to_a.inspect # never reached

# Uncomment if you want to see the users schema
# puts users.dataset.db.schema(:users)

如果您想使用独立关系 classes 而不是容器配置 dsl,那么我建议阅读 Auto Registration system.

数据库创建问题

有一大堆可能会阻止创建 sqlite 数据库的事情。

  • 可能是权限问题
  • 目录结构可能不存在
  • Sqlite 可能无法编译处理 URI(仅当您在路径中使用 file:// 时才重要)[see sqlite docs]

我的建议是在使用 sqlite 和 ROM 时,使用上面脚本中的 opts hash 示例,并尝试使用当前工作目录中的相对路径。这似乎总是有效。