在 Rails 中,当资源创建操作失败并调用 render :new 时,为啥 URL 必须更改为资源的索引 url?
Posted
技术标签:
【中文标题】在 Rails 中,当资源创建操作失败并调用 render :new 时,为啥 URL 必须更改为资源的索引 url?【英文标题】:In Rails when a resource create action fails and calls render :new, why must the URL change to the resource's index url?在 Rails 中,当资源创建操作失败并调用 render :new 时,为什么 URL 必须更改为资源的索引 url? 【发布时间】:2013-01-07 13:05:08 【问题描述】:我有一个名为 Books 的资源。它在我的路由文件中正确列为资源。
我有一个新动作,它为新视图提供了标准:
@book = Book.new
在模型上,有一些属性是通过存在来验证的,所以如果保存操作失败,就会产生错误。
在我的控制器中:
@book = Book.create
... # some logic
if @book.save
redirect_to(@book)
else
render :new
end
这是相当标准的;并且使用 render:new 的基本原理是将对象传递回视图,并且可以报告错误、重新填写表单条目等。
这可行,除非每次我被发送回表单(通过渲染:new),我的错误都会出现,但我的 URL 是 INDEX URL,即
/books
而不是
/books/new
我最初是从那里开始的。我看过其他几个关于这个问题的帖子,但没有答案。至少,有人会假设它会将您带到 /books/create,我也有一个视图文件(在这种情况下与 new 相同)。
我可以这样做:
# if the book isn't saved then
flash[:error] = "Errors!"
redirect_to new_book_path
但是随后@book 数据以及错误消息都会丢失,这就是拥有表单和操作等的全部意义。
为什么当通常该 URL 调用列出所有书籍的 INDEX 方法时,render :new 将我登录到 /books,我的索引操作?
【问题讨论】:
它不会让您进入 /books/new,因为您是通过发布到 /books/ 创建资源的,当它失败时它只是呈现新操作,而不是将您重定向到新操作跨度> 试试redirect_to new_book_path(book: params[:book])
@Doon 是对的(他应该把它作为一个答案,这样我就可以投票了:))。在 REST 的“Rails 视图”中创建操作没有可视化,因此是正常的重定向。这里的错误网址有什么问题?
@MrYoshiji 关闭,但没有雪茄......这给了我一个带有 /books/new/book%skldjalkdja 的 URL(我相信与图书 ID 本身有关的乱码)。 /book/new 怎么样?
【参考方案1】:
它实际上 将您发送到创建路径。它在create
操作中,其路径为/books
,使用HTTP 方法POST。这看起来与索引路径/books
相同,但索引路径使用的是HTTP 方法GET。 rails 路由代码在确定调用哪个操作时会考虑该方法。验证失败后,您仍处于创建操作中,但您正在呈现 new
视图。这有点令人困惑,但像render :new
这样的行实际上根本不会调用新操作;它仍在运行 create 操作,它告诉 Rails 渲染新的 view。
【讨论】:
有没有办法渲染 :new,以便错误传递给表单,同时登陆 URL 中的 /books/new?考虑到 /books/new 是我开始的地方,也是用户期望的地方,似乎这样看起来会更好,也更有意义。 为此,您必须重定向(请参阅上面的@MrYoshiji 评论)。当您提交表单时,浏览器会向create_book_path
发出请求。除非您随后重定向到另一个页面,否则浏览器仍然认为它位于创建 URL。不过,要使其真正成为 RESTful 还需要做一些工作;如果您只是使用 GET 重定向到 new_book_path
,则必须对 URL 中的表单参数进行编码(并不总是理想的,有时对于更复杂/更大的表单数据是不可能的)。 RESTful 方式是为/books/new
添加一个 POST 路由来填充模型/表单数据。
@Doon 建议使用 javascript 更新浏览器中显示的 URL 是一种更有效的解决方案。我不知道如何更改显示的 URL。浏览器可能不会支持它。【参考方案2】:
我刚开始使用Rails-Tutorial 并遇到了同样的问题。 解决方案很简单:如果您在提交表单后想要相同的 URL(有错误),只需将 new 和 create 操作合并到一个操作中。
这是我的代码的一部分,它使这成为可能(希望它可以帮助某人^^)
routes.rb(为新动作添加后路由):
...
resources :books
post "books/new"
...
控制器:
...
def create
@book = Book.new(book_params)
if @book.save
# save was successful
print "Book saved!"
else
# If we have errors render the form again
render 'new'
end
end
def new
if book_params
# If data submitted already by the form we call the create method
create
return
end
@book = Book.new
render 'new' # call it explicit
end
private
def book_params
if params[:book].nil? || params[:book].empty?
return false
else
return params.require(:book).permit(:title, :isbn, :price)
end
end
new.html.erb:
<%= form_for @book, :url => :action => :new do |f| %>
<%= f.label :title %>
<%= f.text_field :title %>
<%= f.label :isbn %>
<%= f.text_field :isbn %>
<%= f.label :price %>
<%= f.password_field :price %>
<%= f.submit "Save book" %>
<% end %>
【讨论】:
非常好的解决方案,感谢分享,不过我有一个问题,我对new
和update
操作使用相同的“_form”模板,所以我正在尝试:@987654327 @ 然后:= form_for @book, :url => :action => action
但它似乎没有正确生成路径。
好的,我想我必须使用url_for @book
来生成 URL,谢谢【参考方案3】:
刚刚有同样的问题,所以也许有一天这可能会对某人有所帮助。您基本上必须进行 3 次调整才能使这件事起作用,尽管我的解决方案仍然不理想。
1) 在创建动作中:
if @book.save
redirect_to(@book)
else
flash[:book] = @book
redirect_to new_book_path
end
2) 在新动作中:
@book = flash[:book] ? Book.new(flash[:book]): Book.new
3) 无论你在哪里解析 flash 哈希,一定要过滤掉 flash[:book]。
--> 显示正确的 URL,保留表单数据。尽管如此,我还是不喜欢将用户对象放入闪存哈希中,我认为这不是它的目的。有没有人知道一个更好的地方放它?
【讨论】:
我用这种方法得到了一个 CookieOverflow,因为它比 cookie 允许的 4k 大 你将如何通过这个传递错误消息? @Gcap 幸运的是,flash
是一个哈希,因此您可以在设置 flash[:book]
后添加其他项目,例如 flash[:error]
或 flash[:alert]
。 Action Controller guide 有更多关于 flash
的详细信息。【参考方案4】:
它不会让您登陆/books/new
,因为您通过发布到/books/
来创建资源。当您的创建失败时,它只是呈现新操作,而不是将您重定向到新操作。正如@MrYoshiji 上面所说,您可以尝试将其重定向到新操作,但这确实效率低下,因为您将创建另一个 HTTP 请求并往返于服务器,只是为了更改 url。到那时,如果它很重要,您可能可以使用 javascript 更改它。
【讨论】:
【参考方案5】:可以通过使用相同的 url 但不同的方法来新建和创建操作来修复它。
在路由文件中可以使用以下代码。
resources :books do
get :common_path_string, on: :collection, action: :new
post :common_path_string, on: :collection, action: :create
end
现在你的新页面将在 url 处呈现
书籍/common_path_string
如果验证后出现任何错误,网址仍然相同。
同样在表单中使用
books_path
使用
url: common_path_string_books_path, method: :post
选择您喜欢的common_path_string。
【讨论】:
以上是关于在 Rails 中,当资源创建操作失败并调用 render :new 时,为啥 URL 必须更改为资源的索引 url?的主要内容,如果未能解决你的问题,请参考以下文章
列存储索引创建失败并出现此错误“资源池'默认'中没有足够的内存来运行此查询”