我在以下关联中有两个模型:客户has_many产品和Product belongs_to客户端。在客户端显示视图中,我提供了一个表单来创建新产品,该表单自动属于当前客户端。客户端控制器中的show方法
def show
@client = Client.find(params[:id])
@products = @client.products.paginate(page: params[:page])
@product = @client.products.new
@product.client_id = @client.id
end
并且show视图呈现部分
<h1>New Product:</h1>
<%= render 'shared/product_form' %>
这样可以正确创建产品。
发生验证错误时,我在产品创建方法中设置了一个闪存,并将redirect_重定向到客户端显示页面。在那里,我放松了正确填写的数据。我尝试保存@product实例变量,该变量包含所有数据(包括错误的字段)
render client_path(client)
来自产品控制器的,但会产生错误
缺少模板/客户端/ 17
地址为
http://localhost:3000/products
我说错了吗?我知道渲染通常会渲染同一个控制器的动作。我可以以某种方式从产品控制器渲染Client :: show吗?是否有其他方法可以保存用户输入的数据?
答案 0 :(得分:5)
如果发生验证错误,您应该重定向回生成验证错误的页面。即:如果用户在提交表单时处于products/new
,那么您的products#create
操作应以render :new
结束,以再次展示产品表单。
如果您的products#create
操作是从clients#show
收到表单,那么您确实希望使用验证错误呈现clients#show
。在这种情况下,表单中填写的所有信息都将在params[:product]
处提供,就像它进入products#create
一样。
你可能想看看我最近写的another answer来了解控制器之间的流程。
具体而言,您案件中的误解如下:
如果您遇到验证错误,则记录将无法保存,因此您无法“返回该数据”,因为您的应用尚未将其保留在任何位置。提交的唯一数据副本位于请求中。
如果您重定向您没有转发请求,则通过向其他URL发出新请求来响应初始POST请求(其中包括所有表单信息为params [:product])。这就是你想使用RENDER的原因。
但是,如果您尝试render clients_path(client)
,Ruby会首先评估clients_path(client)
到字符串clients/(client.id)
,或者您给出的示例clients/17
。< / p>
然后渲染尝试调用您没有模板的render 'clients/17'
。它正在寻找一个名为clients/17.html.erb
的文件。这就是你得到错误的原因。
总而言之,总结一下,您的products#create
操作会收到从表单发送的信息params[:products]
。该控制器操作之外的信息不可用。因此,如果存在验证错误,而不是创建产品,则此控制器操作应呈现用户最初来自的相同页面(通常为products/new
),以便他们可以看到他们刚刚拥有的表单(使用如果您正在使用表单构建器,则会将信息填回其中,并且还会看到阻止保存的错误。
我希望这有意义,随时提出后续问题。
答案 1 :(得分:0)
是的,你说错了。
有三个问题:
render
应该呈现模板名称,例如client/new
,shared/form
等。参数不能是路径或变量。该变量由控制器传递给视图,与render
无关。
您不应使用render
进行保存失败。即使您使用的模板名称为#1,您最终也会在客户端页面中找到错误的网址products/create
。这是不可接受的。
我的建议是始终使用redirect
来保存失败。
小问题。在控制器中,如果您使用了@product = @client.products.new
,则@product
对象将使每个属性都为空但具有有效的客户端ID。因此,您无需再次@product.client_id = @client.id
分配客户端ID。但这不会影响结果。
答案 2 :(得分:0)
安德鲁的好回答加上这个:
Where to render comments controller in Rails on model validations failure?
使解决方案更加清晰。
您的具体示例:请注意create
中的ProductsController
操作包含所需的所有实例变量render 'clients/show'
。