I’m just curious, how would you surface the ability to create nested items in an API? Say you have a model Post that has_many observations.
I have my base routes for both Post and Observations, but in thinking about a client using the API, they’d probably need the UI to have the post form and observation forms in the same view since observations are directly tied to posts. One problem is that when you’re adding a post, it’s new so we can’t just create an observation by sending a POST to /api/v1/observations because we don’t have a post_id to assign to yet. I suppose someone making the client (my future self) could force the creation of a post before adding observations to it, but that feels more like a limitation than anything.
The purpose of accepts_nested_attributes_for is to allow child models (Observation) to be created at the same time as a parent model (Post), which sounds exactly like your use case. You do this by including a collection of child attributes along with the attributes for the parent model in a request to the parent model’s create action.
There are a few steps to enable this behavior.
Add accepts_nested_attributes_for to your Post model
class Post < ActiveRecord::Base
has_many :observations
# add this =>
accepts_nested_attributes_for :observations
end
Update Strong Parameters to allow the nested params
You need to let your controller know to accept the attributes that will be coming in for the nested model.
class PostsController
# ...
def post_params
params.require(:post).permit(
...
observations_attributes: [:attr1, :attr2]
end
end
observation_attributes is a hash key whose value is an array of all the attributes of the Observation which should be allowed.
Important Add inverse_of to the Post model
Here is where people get tripped up. As you pointed out above, an Observation has a foreign key to a Post, but since we are creating both types of records at the same time, we won’t yet have a post_id to associate with.
The inverse_of option sets up a relationship between the parent/child association and lets the Post be created before the Observation objects and then link them together. Without this, if you try sending a request using AFAF, you’ll get back an error "observations.post":["must exist"].
class Post < ActiveRecord::Base
has_many :observations, inverse_of: :post
end