Правильный способ сделать это - определить вложенный маршрут и настроить форму так, чтобы он проводил по этому маршруту. Таким образом, вместо создания отдельной формы, в которой пользователь должен выбрать чай, вы создаете форму на странице показа или для каждого чая на индексной странице, где пользователь может создавать отзывы.
# config/routes.rb
resources :teas do
resources :reviews, shallow: true
end
shallow: true
делает так, чтобы действия члена (показ, редактирование, обновление, уничтожение) не были вложенными.
Затем настройте частичное для формы, чтобы вы могли использовать его повторно:
# app/views/reviews/_form.html.erb
<%= form_for([local_assigns(:tea), review]) do |f| %>
<div class="field">
<%= f.label :rating %>
<%= f.number_field :rating, min:0, max:10 %>
</div>
<div class="field">
<%= f.label :title %>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :content %>
<%= f.text_area :content, size: "60x25" %>
</div>
<div class="actions">
<%= f.submit %>
</div>
<% end %>
By передавая массив, вы получаете вложенный маршрут в качестве атрибута действия (/teas/1/reviews
), и вам не нужно возиться со скрытым вводом. local_assigns(:tea)
избегает ошибки NoMethodError, если она не передана в частичное. Массив сжат, так что этот фрагмент будет работать как для создания, так и для обновления.
# app/views/reviews/new.html.erb
<%= render partial: 'form', tea: @tea, review: @review >
# app/views/reviews/edit.html.erb
<%= render partial: 'form', review: @review >
# app/views/teas/show.html.erb
<h2>Review this tea</h2>
<%= render partial: 'reviews/form', tea: @tea, review: @tea.reviews.new >
В контроллере вы можете просто получить чай из params[:tea_id]
, так как вы передали его в путь.
class ReviewsController < ApplicationController
before_action :set_tea, only: [:new, :index, :create]
before_action :set_review, only: [:show, :edit, :update, :destroy]
# POST /teas/1/reviews
def create
# creating the review off the tea reveals intent better than doing
# it off the user
@review = @tea.reviews.new(review_params) do |r|
r.user = current_user
end
# Always check if the record is actually persisted
# - not just if the applications validations pass!
if @review.save
# you could also redirect to the review but this makes more
# sense from a ux perspective
redirect_to @tea, notice: 'Thank you for your review'
else
render :new
end
end
# GET /reviews/:id/edit
def edit
end
# PUT|PATCH /reviews/:id
def update
if @review.update(review_params)
redirect_to @review, notice: 'Review updated.'
else
render :edit
end
end
private
def set_tea
@tea = Tea.find(params[:tea_id])
end
def set_review
@review = Review.find(params[:id])
end
def review_params
params.require(:review).permit(:rating, :title)
end
end