Building forms in Rails has been a fun exercise of demystifying what happens behind the scenes on your favorite sites. Those fun quizzes you’ve answered and comments left on a blog post are being collected by companies and saved to a database, to be used for whatever purposes later on. Good times.
Forms can be simple endeavors, such as a generic search form or a form to update your account profile page. But oftentimes, forms are complex where you may need to update more than a single object at a time.
Let’s say you have a form that allows you to create a trainer and its new Pokemon at the same time.
With Active Record, it simplifies this process by allowing you to save attributes on associated records through the parent model using the accepts_nested_attributes_for class method. In a one-to-many relationship, the accepts_nested_attributes_for method always lives on the parent/has_many model.
By default, nested attribute updating is turned off so you will have to enable it using accepts_nested_attributes_for. By using this Active Record macro, it will add a new writer method to your model that will be named after the association.
Your trainer model should reflect the has_many Pokemons relationship.
Class Trainer < ActiveRecord::Base
Your controller should be updated to declare the permitted parameters before you pass the parameters to the model.
As for when you might need to write a custom association writer method, #accepts_nested_attributes_for is not able to help us avoid duplicates. For the purposes of this exercise, a trainer can only have one Pokemon of the same name, just like the Pokemon universe hero Ash Ketchum.
Our custom method, which will be written in the trainer model, will use the Active Record query method find_or_create_by to look to see if that Pokemon already exists in the database, and if not, will instantiate that Pokemon for us and associate it to our trainer by adding it to the trainer’s Pokemon collection.
An important thing to note is that accepts_nested_attributes_for and custom setter methods are not necessarily mutually exclusive. You will have to evaluate the needs of your specific use case and go with the approach that makes the most sense for your application.