24

I have a rather large json document that I have to store in a field for each Evaluation instance in my app. The over time, certain actions on the app will require me to change various key/value pairs in the document. The Rails 4 and PostgreSQL json data type seems ideal for this problem, but I cannot get changes to commit to the database.

Here's an abbreviated workflow:

I have a json file that I import into a field for each Evaluation record:

// example.json
{ "question_1":"no" }

I create a record and import the json:

>> evaluation = Evaluation.create assessments: File.read("example.json")
>> evaluation.assessments = File.read("#{Rails.root}/example.json")
=> "{ \"question_1\":\"no\" }"

Calling on the assessments field seems to work fine:

>> evaluation.assessments
=> {"question_1"=>"no"}

OR

>> evaluation.assessments["question_1"]
=> "no"

Example 1

Changing the json isn't working too well. This doesn't actually commit anything:

>> evaluation.assessments["question_1"] = "yes"
=> "yes"

>> evaluation.assessments["question_1"]
=> "yes"

>> evaluation.save
   (0.3ms)  BEGIN
   (0.2ms)  COMMIT
=> true

Example #2

Replacing with a completely new object does commit:

>> evaluation.assessments = {"question_1"=>"yes"}
=> {"question_1"=>"yes"}

>> evaluation.save
(0.3ms)  BEGIN
SQL (0.7ms)  UPDATE "evaluations" SET "assessments" = $1, "updated_at" = $2 WHERE "evaluations"."id" = 1  [["assessments", "{\"question_1\":\"yes\"}"], ["updated_at", "2014-08-21 00:52:03.581817"]]
(3.8ms)  COMMIT
=> true
  • What am I doing wrong in Example #1?
  • Why won't the database commit the change I made to the evaluation.assessment column when I call it, change the value, and call save on the instance?
1

1 Answer 1

39

This ended up being an easy fix due to a known bug related to ActiveRecord not knowing the changed attribute is dirty:

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

Here's the solution:

>> evaluation.assessments["question_1"] = "yes"
=> "yes"

>> evaluation.assessments_will_change!
>> evaluation.save
(1.1ms)  BEGIN
SQL (1.6ms)  UPDATE "evaluations" SET "assessments" = $1, "updated_at" = $2 WHERE "evaluations"."id" = 4  [["assessments", "{\"question_1\":\"yes\"}"], ["updated_at", "2014-08-21 01:59:47.331216"]]
(2.5ms)  COMMIT
=> true
Sign up to request clarification or add additional context in comments.

4 Comments

Thanks, I was struggling with this. Didn't even thought to consider ActiveRecord was at fault ...
I think that this problem/solution was rendered irrelevant with the Rails 4.2 update, but I'm not completely sure.
This was mind numbing. Couldn't figure out for the life of me why this wasn't working especially when it would persist on the model but not if I did a .reload
In Rails 5, a simple merge! followed by a save will do the trick - whether you are updating an existing key or adding a new one.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.