'Rails: JSON round trip from database to TEXTAREA converts to string on second save
(Rails 5.2) My record with JSON is saving correctly to the (Postgres 9.4) database on CREATE but is getting reformatted after UPDATE, with carriage returns (\r) and newline characters (\n) showing up in the database after UPDATE.
The :budget_json column is a jsonb type.
Here is my (simplified) Budget model:
class Budget < ApplicationRecord
after_initialize :create_shell_json
def create_shell_json
self.budget_json = blank_budget if self.new_record?
end
def blank_budget
{
id: nil,
name: nil,
year: nil,
[etc...]
}
end
end
Here is my controller:
def new
@budget = Budget.new(year: Time.now.year)
end
def create
@budget = Budget.new(budget_params)
@budget.budget_json = JSON.parse(budget_params[:budget_json])
if @budget.save
redirect_to admin_budgets_path, notice: "Budget successfully created."
else
render :new
end
end
def edit
@budget = Budget.find(params[:id])
end
def update
@budget = Budget.find(params[:id])
@budget.budget_json = JSON.parse(budget_params[:budget_json])
if @budget.update(budget_params)
redirect_to admin_budgets_path, notice: "Budget successfully updated."
else
render :edit
end
end
And here are the relevant parts of the form. (The form is the same for CREATE and UPDATE.) The TEXTAREA contains the editable JSON should the user want to amend the default values:
<%= form_with model: [:admin, @budget], local: true, :html => {:class => "form-horizontal"} do |f| %>
...
<div class="form-group">
<div class="col-sm-2">
<%= f.label :budget_json %>
</div>
<div class="col-sm-2">
<%= text_area_tag "budget[budget_json]", JSON.pretty_generate(@budget.budget_json), id: "budget_budget_json" %>
</div>
</div>
...
<% end %>
FWIW, the form looks like this:
As you can see here (from pgAdmin), the first record (id: 166) is clean and usable. It has only just been created. The second record (id: 167) is unusable as has been stored as a string instead:
What am I missing?
Solution 1:[1]
Jeez. How often does writing the whole thing out help you think more clearly! I have the answer: in the UPDATE action I wasn't actually using the JSON.parsed version of the parameters. By changing
if @budget.update(budget_params)
to
if @budget.save(budget_params)
everything works as it should.
Having said that, if anyone is able to suggest a more elegant way of coding these (admin interface) round trips for JSON data, I'll be happy to hear your suggestions.
Solution 2:[2]
Ran into something similar, but managed to get around by modifying the params before passing them to the update method. So in your case something like:
def update
params = budget_params
params[:budget_json] = JSON.parse(params[:budget_json])
if @budget.update(params)
redirect_to admin_budgets_path, notice: "Budget successfully updated."
else
render :edit
end
end
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | MSC |
Solution 2 | sn3p |