Working with JSON objects in Rails form
I’m a big fan of PostgreSQL and its JSON field type is a versatile, especially paired with relational data. It can be a great solution to allow users create and update records with variety keys for the same object.
In Rails, I want to be able to accept, store, and update a JSON field from a web form. How can I do it?
By taking advantage of Rails’ conventions and working with strong parameters.
Creating
HTML Input Convention
Any HTML input field following the format will associate with a model’s JSON field:
id='<model>_<JSON field>'
name='<model>[<JSON field>]'
By doing this, one can store multiple identifiers for a book:
- Model: Book
- Field: Identifier
The Rails form for this would look like:
id='book_identifier'
name='book_identifier'
And the corresponding HTML <input>
field would be:
<input type='text' id='book_identifier' name='book_identifier'>
Rails Parameters
When submitting the form, Rails’ params
will be:
params = {'book' => {'identifier' => {'isbn-10' => '0-590-76484-5'}, {'isbn-13' => '973-0-590-76484-5'}},
'controller' => 'book/entry'}
Strong Parameters
With Rails’ strong params, serializing the parameters to JSON once so the system will be safe from SQL-injection like attacks
params['book']['identifier'] = params['book']['identifier'].to_json
book_create_params = params.require(:book).permit(:identifier)
Book.create(book_create_params)
Updating
Updating Whole Field
When updating the whole field, follow the same pattern as creating the field.
params = {'id' => 1,
'book' => {'identifier' => {'isbn-10' => '0-590-76484-6'}, {'isbn-13' => '973-0-590-76484-6'}},
'controller' => 'book/entry'}
params['book']['identifier'] = params['book']['identifier'].to_json
book_update_params = params.require(:book).permit(:identifier)
book = Book.find_by_id(params['id'])
book.update(book_update_params)
Updating Individual Field
HTML Input Convention
When updating an individual JSON key of a model’s field, first the HTML input must be setup correctly by extending the convention:
id='<model>_<JSON field>_<JSON field key>'
name='<model>[<JSON field>][<JSON field key>]'
By doing this, one can store both version of a book’s ISBN code (10 or 13) in a single JSON field, instead of two:
- Model: Book
- Field: Identifier
- JSON field key: ISBN-10
- JSON field key: ISBN-13
- Field: Identifier
Following the above example, the Rails form for this would be:
id='book_identifier_isbn-10'
name='book_identifier_isbn-10'
And the corresponding HTML <input>
field would be:
<input type='text' id='book_identifier_isbn-10' name='book_identifier_isbn-10'>
<input type='text' id='book_identifier_isbn-13' name='book_identifier_isbn-13'>
Strong Parameters
To update individual field, while keeping other fields the same, requires a merge with the existing values, then serializing the whole object:
params = {'id' => 1,
'book' => {'identifier' => {'isbn-10' => '0-590-76484-6'}},
'controller' => 'book/entry'}
book = Book.find_by_id(params['id'])
params['book']['identifier'] = book.identifier.merge(params['book']['identifier']).to_json
book_update_params = params.require(:book).permit(:identifier)
book.update(book_update_params)
Conclusion
Working with JSON objects and Rails’ forms is safe & easy, by following Rails conventions. Form users will be able to create and update fields of data with any keys, without continuous database migrations.