Often when creating a web application there comes a time where two models are related and you want to create one form where you can add both the attributes. Let’s say you have a topic model which has many questions, and you want to create the topic and questions for it in one form. Rails makes this easy with the accepts_nested_attributes_for method.
Models
1 2 3 |
|
1 2 3 |
|
In order for the topic form to be able to add questions, we need the following line:
1 2 3 4 |
|
Accepts nested attributes is just a shortcut, it defines a dynamic attribute {field_name}_attributes so you can automatically assign them to an association. The allow destroy lets you destroy question objects in this case through the form, it is set to false from the start. There are other methods you can use with this, such as reject_if, limit and update_only. More information can be found from the source.
Form
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
|
This creates a simple form which allows you to create a topic with a name and add one question to it.
Controller & Routes
The controller method for create is your regular create method, nothing fancy. That’s why I love Rails.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
The new method creates the instances for both topic and questions. In the create action we instantiate a topic passing in a method. This is a private method needed in Rails 4 to make sure only the allowed attributes are used. We require the topic parameters and permit the name for a topic and the questions_attributes. Now all we need are the routes and this will work.
1
|
|
Now we have a simple version of adding questions to a topic, but we still don’t have validations.
Validations
It is good practice to have your validations on both the database level and on the Rails model’s itself. The validation for the association is not as straightforward as it seems. A common approach would look like this.
1 2 3 4 5 6 |
|
1 2 3 4 |
|
These are simple validations that would normally pass but they won’t because of the nested forms. You need to tell Rails that the two model relations describe the same relationship but from the opposite direction. Rails provides a method for this, inverse_of. Rails has a method for everything.
1 2 3 4 5 6 |
|
1 2 3 4 |
|
As you can see we indicated the inverse of relation from both sides. Also now the Question model validates the topic itself instead of the ID.
There you have it, a form that accepts nested attributes in Rails 4 with validations.