更新属性后ActiveRecord不保存

时间:2022-06-13 11:18:05

I'm wondering if there is something at work here that I don't understand or if I've run into a bug in ActiveRecord (4.1.1).

我想知道在这里是否有一些我不理解的东西,或者我是否遇到过ActiveRecord(4.1.1)中的错误。

I have a database full of records with only one attribute, a field a bit of JSON in it. I take one and try to update it like so.

我有一个数据库,里面只有一个属性,一个JSON字段。我拿一个并尝试更新它。

test = Submission.find(1)
test.update_attribute('json_data',similar_but_different_json(test.json_data))

Let's assume the method similar_but_different_json makes a small update to that JSON. In my case I'm fixing some data errors that were created by a broken form.

我们假设方法similar_but_different_json对该JSON进行了一次小的更新。在我的情况下,我正在修复由损坏的表单创建的一些数据错误。

When doing this, I don't get any errors, I show a commit in the console but no data submitted and get a return of true.

执行此操作时,我没有收到任何错误,我在控制台中显示提交但未提交数据并返回true。

In order to actually update the record I have to do this.

为了实际更新记录,我必须这样做。

test = Submission.find(1)
old_json_data = test.json_data
test.json_data = ""
test.json_data = similar_but_different_json(old_json_data)
test.save

What seems to be happening is that ActiveRecord doesn't identify that a change has been made that has to be saved. Could this be why setting the field to an empty string then back to JSON allows the record to save?

似乎正在发生的事情是ActiveRecord没有确定已经进行了必须保存的更改。这可能是为什么将字段设置为空字符串然后返回JSON允许记录保存?

3 个解决方案

#1


8  

will_change!

You can also use:

您还可以使用:

test.json_data_will_change!   # Goes before the save.

This will tell ActiveModel that the attribute, json_data, has changed (i.e. it's dirty ← there's a joke there somewhere) and will update the value properly on save.

这将告诉ActiveModel属性json_data已经改变(即它是脏的←那里有一个笑话)并将在保存时正确更新值。

See Rails is not saving an attribute that is changed for some more details as well.

有关更多详细信息,请参阅Rails未保存已更改的属性。

#2


1  

I don't understand why exactly the object is not marked dirty. A workaround is to use update_columns:

我不明白为什么对象没有标记为脏。解决方法是使用update_columns:

test.update_columns(json_data: similar_but_different_json(test.json_data))

It will execute an UPDATE query directly in the DB, without any validation, dirty check, etc... The json_data field must not be read-only though.

它将直接在DB中执行UPDATE查询,无需任何验证,脏检查等...但json_data字段不能是只读的。

#3


1  

ActiveModel (by 4.1.1) doesn't have a way to track "inline" modifications on attributes.

ActiveModel(通过4.1.1)无法跟踪属性上的“内联”修改。

Your 'similar_but_different_json' method is probably making inline modifications on the string.

你的'similar_but_different_json'方法可能会对字符串进行内联修改。

Just duplicate the string before modifying it.

只需复制字符串,然后再修改它。

test = Submission.find(1)
test_json_data_duplicate = test.json_data.dup 
test.update_attribute('json_data',similar_but_different_json(test_json_data_duplicate))

When you did ...

你什么时候......

test.json_data = ""

... ActiveModel could catch the change because you are setting it to a new String object that happens to be empty. So when you call update_attribute the model has already known that the attribute has changed.

... ActiveModel可以捕获更改,因为您将其设置为恰好为空的新String对象。因此,当您调用update_attribute时,模型已经知道该属性已更改。

If you try to empty the string in an inline manner your trick will not work.

如果您尝试以内联方式清空字符串,那么您的技巧将无效。

test = Submission.find(1)
old_json_data = test.json_data
test.json_data.clear # Instead of test.json_data = ""
test.json_data = similar_but_different_json(old_json_data)
test.save

ActiveModel::Dirty

#1


8  

will_change!

You can also use:

您还可以使用:

test.json_data_will_change!   # Goes before the save.

This will tell ActiveModel that the attribute, json_data, has changed (i.e. it's dirty ← there's a joke there somewhere) and will update the value properly on save.

这将告诉ActiveModel属性json_data已经改变(即它是脏的←那里有一个笑话)并将在保存时正确更新值。

See Rails is not saving an attribute that is changed for some more details as well.

有关更多详细信息,请参阅Rails未保存已更改的属性。

#2


1  

I don't understand why exactly the object is not marked dirty. A workaround is to use update_columns:

我不明白为什么对象没有标记为脏。解决方法是使用update_columns:

test.update_columns(json_data: similar_but_different_json(test.json_data))

It will execute an UPDATE query directly in the DB, without any validation, dirty check, etc... The json_data field must not be read-only though.

它将直接在DB中执行UPDATE查询,无需任何验证,脏检查等...但json_data字段不能是只读的。

#3


1  

ActiveModel (by 4.1.1) doesn't have a way to track "inline" modifications on attributes.

ActiveModel(通过4.1.1)无法跟踪属性上的“内联”修改。

Your 'similar_but_different_json' method is probably making inline modifications on the string.

你的'similar_but_different_json'方法可能会对字符串进行内联修改。

Just duplicate the string before modifying it.

只需复制字符串,然后再修改它。

test = Submission.find(1)
test_json_data_duplicate = test.json_data.dup 
test.update_attribute('json_data',similar_but_different_json(test_json_data_duplicate))

When you did ...

你什么时候......

test.json_data = ""

... ActiveModel could catch the change because you are setting it to a new String object that happens to be empty. So when you call update_attribute the model has already known that the attribute has changed.

... ActiveModel可以捕获更改,因为您将其设置为恰好为空的新String对象。因此,当您调用update_attribute时,模型已经知道该属性已更改。

If you try to empty the string in an inline manner your trick will not work.

如果您尝试以内联方式清空字符串,那么您的技巧将无效。

test = Submission.find(1)
old_json_data = test.json_data
test.json_data.clear # Instead of test.json_data = ""
test.json_data = similar_but_different_json(old_json_data)
test.save

ActiveModel::Dirty