Red Green Repeat Adventures of a Spec Driven Junkie

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

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.