序列化时无法将空数组保存到数据库

hsi*_*dar 6 ruby arrays serialization ruby-on-rails

在Ruby on Rails中,.save()如果数组是空的,那么带有序列化数组字段的模型将不会更新,之前它有数据.

我正在使用:

  • Ruby 2.2.1
  • Rails 4.2.1
  • sqlite3 1.3.10

    我创建了一个新的模型,其字段设置为文本:

    rails g model用户名:string example:text

在User.rb文件中,我添加了:

serialize :example, Array
Run Code Online (Sandbox Code Playgroud)

我实例化了User类的新实例:

test = User.new
<User id: nil, name: nil, example: [], created_at: nil, updated_at: nil>
Run Code Online (Sandbox Code Playgroud)

然后我保存用户以确保它正确保存:

test.save()
(0.1ms)  begin transaction
SQL (0.4ms)  INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?)  [["created_at", "2015-05-27 16:17:31.902342"], ["updated_at", "2015-05-27 16:17:31.902342"]]
(0.7ms)  commit transaction
=> true
Run Code Online (Sandbox Code Playgroud)

并添加了一些数据,以使其感到愉快和有目的:

test.example.push(1)
test.example.push(2)
Run Code Online (Sandbox Code Playgroud)

并保存起来:

test.save()
(0.1ms)  begin transaction
SQL (0.3ms)  UPDATE "users" SET "example" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["example", "---\n- 1\n- 2\n"], ["updated_at", "2015-05-27 16:17:50.331777"], ["id", 1]]
(0.8ms)  commit transaction
=> true
Run Code Online (Sandbox Code Playgroud)

并确保一切都很好地保存:

test
<User id: 1, name: nil, example: [1, 2], created_at: "2015-05-27 16:17:31", updated_at: "2015-05-27 16:17:50">
Run Code Online (Sandbox Code Playgroud)

我删除了一个项目,验证它已被删除并保存,确保SQL输出显示UPDATE:

test.example.delete(1)
 => 1
test
<User id: 1, name: nil, example: [2], created_at: "2015-05-27      16:17:31", updated_at: "2015-05-27 16:17:50">
test.save()
(0.1ms)  begin transaction
SQL (0.9ms)  UPDATE "users" SET "example" = ?, "updated_at" = ? WHERE "users"."id" = ?  [["example", "---\n- 2\n"], ["updated_at", "2015-05-27 16:18:30.148553"], ["id", 1]]
(8.9ms)  commit transaction
=> true
Run Code Online (Sandbox Code Playgroud)

我从数组中删除了最后一段数据,验证了空数组并保存了它.请注意缺少UPDATE操作并返回true:

test.example.delete(2)
=> 2
test
<User id: 1, name: nil, example: [], created_at: "2015-05-27 16:17:31", updated_at: "2015-05-27 16:18:30">
test.save()
(0.1ms)  begin transaction
(0.1ms)  commit transaction
=> true
Run Code Online (Sandbox Code Playgroud)

多次保存可获得相同的结果.新的User对象仍然包含最后一段数据:

test = User.find(1)
User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1  [["id", 1]]
<User id: 1, name: nil, example: [2], created_at: "2015-05-27 16:17:31", updated_at: "2015-05-27 16:18:30">
Run Code Online (Sandbox Code Playgroud)

解决方法是从模型中的序列化行中删除"数组",将字段初始化为nil.但这意味着我第一次将数据添加到新实例时,我必须手动将字段设置为空数组(test.example = [])以便调用.push()它.在这个设置中一切正常,新清空的数组可以很好地保存到数据库中.

我在Rails Github上看到一个封闭的问题,表明应该始终保存序列化列,但不知道这是否相关:

https://github.com/rails/rails/issues/8328

我无法从可能照亮我的序列化源代码中辨别出任何内容:

http://apidock.com/rails/ActiveModel/Serializers/Xml/Serializer/serialize

为什么在序列化行末尾添加"Array"会导致空数组不能保存到数据库中?

Abr*_*gha 1

SQLite 不支持数组列类型。我认为发生的情况是,当您尝试使用示例属性的空数组保存用户时,它被解释为示例列没有更改。如果您在使用示例数据创建测试用户后尝试此操作,会发生什么情况?

test.example = nil
test.save
Run Code Online (Sandbox Code Playgroud)

或者,这会发生什么?

test.example = [nil]
test.save
Run Code Online (Sandbox Code Playgroud)

解决方案似乎是使用ActiveRecord 回调,例如before_save检查用户模型的示例属性以确定它是否为空数组。如果是这样,请将属性设置为nil[nil](以有效者为准),然后数据应相应地保留。