I've managed to get nested many-to-many entities created through the use of accepts_nested_attributes_for
for the JSON and the project below.
My question is - is there a suggested way to achieve the same thing where I don't have to add _attributes
to be able to create the array of emotion
objects on the RoR server? What I mean by suggested is either something the framework itself suggests or otherwise something as clean as possible.
JSON getting posted to http://localhost:3000/periods:
{
"period": {
"date": "2022-03-17T03:00:52.820Z",
"period": "morning",
"emotions_attributes": [
{
"name": "ok"
},
{
"name": "fine"
}
]
}
}
rails generate
commands used to create subsequent files:
rails g model Emotion name:string
rails g scaffold Period date:date period:string --skip-template-engine
rails g scaffold Entry emotion:references period:references --skip-template-engine
periods.rb:
class Period < ApplicationRecord
has_many :entries
has_many :emotions, through: :entries
accepts_nested_attributes_for :emotions
end
emotion.rb
class Emotion < ApplicationRecord
has_many :entries
has_many :periods, through: :entries
end
entry.rb
class Entry < ApplicationRecord
belongs_to :emotion
belongs_to :period
end
periods_controller.rb
class PeriodsController < ApplicationController
before_action :set_period, only: %i[ show edit update destroy ]
skip_before_action :verify_authenticity_token
...
def create
@period = Period.new(period_params)
respond_to do |format|
if @period.save
format.html { redirect_to period_url(@period), notice: "Period was successfully created." }
format.json { render :show, status: :created, location: @period }
else
format.html { render :new, status: :unprocessable_entity }
format.json { render json: @period.errors, status: :unprocessable_entity }
end
end
end
...
# FYI turns out the id is key here to be able to even perform create
def period_params
params.require(:period).permit(
:date,
:period,
emotions_attributes: [:id, :name]
)
end
routes.rb
Rails.application.routes.draw do
resources :entries
resources :periods
What I'd like to be able to post is this:
{
"period": {
"date": "2022-03-17T03:00:52.820Z",
"period": "morning",
"emotions": [
{
"name": "ok"
},
{
"name": "fine"
}
]
}
}
CodePudding user response:
you won't find the cleaner way to achieve that cause "emotions_attributes" are handled "out of the box" by the framework.
alternatively you can create an service object for more custom period creation (fe app/services/periods/create.rb) and call it from controller. This way you can customize default convention and keep the logic in one place.
But imho you should stay with the default convention as long as you are just starting your journey with rails and the main concern is that "emotions looks better than emotions_attributes".
CodePudding user response:
Well, you could permit your params like this:
def period_params_hand
params.require(:period).permit(
:date,
:period,
emotions: [:id, :name]
)
end
And then the first thing you could do in #create is:
# receive the permitted params
period_params = period_params_hand
# rename the hash key
period_params[:emotions_attributes] = period_params.delete(:emotions)
This is a way to achieve your goal, but answering your question, there is no suggested way of doing that.