При использовании неглубоких маршрутов разные маршруты требуют разных аргументов form_for
Я использую Simple Form здесь, но это проблема с обычными формами Rails. При использовании неглубоких маршрутов form_for нужны разные аргументы в зависимости от того, в каком контексте он использовался.
Пример: для редактирования (http://localhost:3000/notes/2/edit
), _form.html.erb должен иметь simple_form_for(@note)
. Но для создания новой заметки (http://localhost:3000/customers/2/notes/new
) _form.html.erb требуется simple_form_for([@customer, @note])
. Если либо принимает неправильные аргументы, я получаю метод, который не найден.
Какой лучший способ справиться с этим?
- Я мог бы сделать две отдельные формы, но это кажется беспорядочным.
- Мне нужно установить @customer для обратной ссылки, но я мог бы использовать другую форму в форме (например, @customer_form) и просто не устанавливать ее в методах редактирования и обновления, но это противоречиво и немного запутывает, поскольку В новом методе мне нужно будет установить как @customer_form, так и @customer.
- Я мог бы сделать то, что этот парень сделал и разделил форму на несколько файлов. Пока это лучший вариант, но мне это не очень нравится, поскольку вы не можете просто открыть _form.html.erb и посмотреть, что происходит.
Являются ли эти мои варианты?
Пример:
конфигурации /routes.rb
Billing::Application.routes.draw do
resources :customers, :shallow => true do
resources :notes
end
end
рейк-маршруты | grep note
customer_notes GET /customers/:customer_id/notes(.:format) notes#index
POST /customers/:customer_id/notes(.:format) notes#create
new_customer_note GET /customers/:customer_id/notes/new(.:format) notes#new
edit_note GET /notes/:id/edit(.:format) notes#edit
note GET /notes/:id(.:format) notes#show
PUT /notes/:id(.:format) notes#update
DELETE /notes/:id(.:format) notes#destroy
приложение/просмотров/примечания/_form.html.erb
# v----------------------------- Right here
<%= simple_form_for (@note), html: { class: 'form-vertical'} do |f| %>
<%= f.input :content %>
<%= f.button :submit %>
<% end -%>
приложение/просмотров/примечания/new.html.erb
<h1>New note</h1>
<%= render 'form' %>
<%= link_to 'Back', customer_path(@customer) %>
приложение/просмотров/примечания/edit.html.erb
<h1>Editing note</h1>
<%= render 'form' %>
<%= link_to 'Show', @note %>
<%= link_to 'Back', customer_path(@customer) %>
приложение/контроллеры/notes_controller.rb
class NotesController < ApplicationController
def show
@note = Note.find(params[:id])
@customer = Customer.find(@note.customer_id)
respond_to do |format|
format.html
format.json {render json: @note }
end
end
# GET /notes/new
# GET /notes/new.json
def new
@note = Note.new
@customer = Customer.find(params[:customer_id])
respond_to do |format|
format.html # new.html.erb
format.json { render json: @note }
end
end
# GET /notes/1/edit
def edit
@note = Note.find(params[:id])
@customer = Customer.find(@note.customer_id)
end
# POST /notes
# POST /notes.json
def create
@customer = Customer.find(params[:customer_id])
@note = @customer.notes.build(params[:note])
respond_to do |format|
if @note.save
format.html { redirect_to @customer, notice: 'Note was successfully created.' }
format.json { render json: @note, status: :created, location: @note }
else
format.html { render action: "new" }
format.json { render json: @note.errors, status: :unprocessable_entity }
end
end
end
# PUT /notes/1
# PUT /notes/1.json
def update
@note = Note.find(params[:id])
@customer = Customer.find(@note.customer_id)
respond_to do |format|
if @note.update_attributes(params[:note])
format.html { redirect_to @customer, notice: 'Note was successfully updated.' }
format.json { head :no_content }
else
format.html { render action: "edit" }
format.json { render json: @note.errors, status: :unprocessable_entity }
end
end
end
# DELETE /notes/1
# DELETE /notes/1.json
def destroy
@note = Note.find(params[:id])
@note.destroy
respond_to do |format|
format.html { redirect_to :back }
format.json { head :no_content }
end
end
end
Ответы
Ответ 1
Если первый объект в массиве, который вы передаете построителю формы, nil
, Rails будет отправлять POST только второму объекту. По этой причине просто не устанавливайте свой @customer
объект в действии редактирования контроллера. Если вам нужен доступ к объекту клиента, вызовите его через @note
.
Если вы используете одно и то же частичное для нового и редактируете, вам нужно установить @note.customer
в новое действие контроллера (@customer
не будет задано при редактировании).
Я думаю, именно так команда Rails намеревалась работать.
Ответ 2
Я хотел бы предложить небольшую модификацию решения Джеймса:
# app/helpers/application_helper.rb
def shallow_args(parent, child)
child.try(:new_record?) ? [parent, child] : child
end
Вместо того, чтобы полагаться на действие контроллера, называемое "новым", хотя оно, вероятно, будет в 95% случаев - это просто проверяет, является ли ребенок новой записью.
Ответ 3
Вот что я придумал:
приложение/хелперы/application_helper.rb
module ApplicationHelper
# Public: Pick the correct arguments for form_for when shallow routes
# are used.
#
# parent - The Resource that has_* child
# child - The Resource that belongs_to parent.
def shallow_args(parent, child)
params[:action] == 'new' ? [parent, child] : child
end
end
приложение/просмотров/примечания/_form.html.erb
<%= simple_form_for shallow_args(@customer, @note), html: { class: 'form-vertical'} do |f| %>
<%= f.input :content %>
<%= f.button :submit %>
<% end -%>
Я не знаю, что это лучшее решение, но, похоже, все в порядке.