【问题标题】:rails render form after error in other controllerrails 在其他控制器中出现错误后呈现表单
【发布时间】:2013-04-21 15:26:56
【问题描述】:

我在以下关系中有两个模型:Client has_many products 和 Product belongs_to client。在客户端显示视图中,我展示了一个用于创建新产品的表单,该新产品自动属于当前客户端。客户端控制器中的 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

并且显示视图呈现部分

    <h1>New Product:</h1>
    <%= render 'shared/product_form' %>

这行得通,产品已正确创建。

当发生验证错误时,我在产品创建方法中设置了一个 flash 并重定向到客户端显示页面。在那里,我丢失了已正确填写的数据。我尝试保存@product 实例变量,其中包含所有数据(包括错误字段)

render client_path(client) 

来自产品控制器,但这会产生错误

缺少模板 /clients/17

地址是

http://localhost:3000/products

我说错了吗?我知道渲染通常会渲染同一个控制器的动作。我可以以某种方式从产品控制器呈现 Client::show 吗?还有其他方法可以保存用户输入的数据吗?

【问题讨论】:

    标签: ruby-on-rails-3 controller render


    【解决方案1】:

    如果发生验证错误,您应该重定向回产生验证错误的页面。即:如果用户在提交表单时位于products/new,那么您的products#create 操作应以render :new 结束以再次呈现产品表单。

    如果您的 products#create 操作正在接收来自 clients#show 的表单,那么您确实希望呈现带有验证错误的 clients#show。在这种情况下,表格中填写的所有信息都将在params[:product] 上提供,就像在products#create 中一样。

    您可能想看看我最近写的another answer,以了解控制器之间的流程。

    具体来说,你的案例中的误解如下:

    当您遇到验证错误时,记录将不会保存,因此您无法“返回该数据”,因为您的应用没有将其保存在任何地方。提交的数据的唯一副本在请求中。

    如果您 REDIRECT 您没有转发请求,则您通过向不同的 url 发出新请求来响应初始 POST 请求(包括所有表单信息作为 params[:product])。这就是您要使用 RENDER 的原因。

    但是,如果您尝试 render clients_path(client),会发生什么情况是 Ruby 将首先将 clients_path(client) 评估为字符串 clients/(client.id),或者在您给出的示例中,clients/17

    然后,render 会尝试调用 render 'clients/17',但您没有模板。它正在寻找一个名为clients/17.html.erb 的文件。这就是为什么你会得到你得到的错误。

    因此,再次总结一下 - 您的 products#create 操作接收从表单发送为 params[:products] 的信息。该信息在此控制器操作之外不可用。因此,如果出现验证错误,而不是创建产品,此控制器操作应呈现用户最初来自的同一页面(通常为 products/new),以便他们可以看到他们刚刚拥有的表单(填充信息如果您使用的是表单生成器,请返回其中)并查看阻止保存的错误。

    我希望这是有道理的,请随时提出后续问题。

    【讨论】:

    • 所以我想我明白了:我将上面控制器中的行更改为 @product=@client.products.new(params[:product]) 应该填充了实例变量。然后在 form_for(@product) 中使用@product,在编辑视图中它会正确填充字段。但这不起作用......我现在做错了什么?
    • params[:product] 不会穿越重定向 - 它是您的控制器响应的原始请求的一部分。您需要从表单发布到的操作中呈现用户正在填写的表单。如果这仍然没有意义,请发布您的两个控制器并指定用户在填写表单时从哪个视图开始。
    • 现在我明白了,它可以工作了。很多东西让你头晕目眩...... THX!
    【解决方案2】:

    是的,你说错了。

    存在三个问题:

    1. render 应呈现模板名称,例如 client/newshared/form 等。参数不能是路径或变量。变量由控制器传给view,与render无关。

    2. 您不应该使用render 保存失败。即使您使用#1 提到的模板名称,您最终也会在客户端页面中输入错误的 URL,例如 products/create。这是不可接受的。

      我的建议是始终使用redirect 保存失败。

    3. 小问题。在控制器中,如果您使用了@product = @client.products.new@product 对象的所有属性都将为空,但具有有效的客户端 ID。所以你不需要通过@product.client_id = @client.id 再次分配客户端ID。但这不会影响结果。

    【讨论】:

    • 我最初使用的 redirect_to 没有按预期填写字段,所以对于我的问题,我尝试了渲染。正如安德鲁所说,我在控制器代码中再次使用重定向 @product=@client.products.new(params[:product]) 。但这也行不通。 params[:product] 是空的吗??
    • 您应该使用product_id。但是使用redirect :back 就足够了。
    【解决方案3】:

    安德鲁的好答案加上这个:
    Where to render comments controller in Rails on model validations failure?
    使解决方案更清晰。

    您的具体示例:请注意您在 ProductsController 中的 create 操作具有它需要的所有实例变量到 render 'clients/show'

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-10
      • 2017-08-12
      相关资源
      最近更新 更多