在Rails 3中,当资源创build操作失败并调用render:new时,为什么URL必须更改为资源的索引url?
我有一个名为书籍的资源。 它在我的路由文件中被正确地列为资源。
我有一个新的行动,给新的观点的标准:
@book = Book.new
在模型上,有一些由存在validation的属性,所以如果保存操作失败,将会产生错误。
在我的控制器中:
@book = Book.create ... # some logic if @book.save redirect_to(@book) else render :new end
这是非常标准的; 以及使用render:new的原理是这样的,这个对象被传回到视图,错误可以被报告,重新填写表单等等。
这有效,除了每次我被发回到表单(通过渲染:新),我的错误显示,但我的url是INDEXurl,这是
/books
而不是
/books/new
我是从哪里开始的。 我看过其他几个关于这个问题的post,但没有答案。 至less,人们会认为它将着陆在/书/创build,我也有一个查看文件(在这种情况下相同的新)。
我可以做这个:
# if the book isn't saved then flash[:error] = "Errors!" redirect_to new_book_path
但是,然后@book数据丢失,以及错误信息,这是forms和行动的整个点等。
为什么渲染:新登陆我在/书,我的索引行动,通常情况下,该URL调用INDEX方法,其中列出所有的书籍?
它实际上是送你到创buildpath。 它在create
操作中,其path是/books
,使用HTTP方法POST。 这看起来与索引path/books
,但索引path使用HTTP方法GET。 在确定要调用哪个动作时,导轨路由代码将该方法考虑在内。 validation失败后,您仍然处于创build操作中,但您正在呈现new
视图。 这有点令人困惑,但像render :new
一样render :new
实际上并没有调用新的动作; 它仍在运行创build操作,它告诉Rails渲染新的视图 。
我刚开始使用Rails-Tutorial ,遇到同样的问题。 解决scheme很简单:如果您在提交表单(错误)后需要相同的URL,只需在一个操作中合并新build和创build操作。
这是我的代码的一部分,这使得这成为可能(希望它可以帮助别人^^)
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 %>
刚刚有同样的问题,所以也许这有可能帮助某个人。 你基本上必须做3次调整才能使这个东西起作用,尽pipe我的解决scheme还不够理想。
1)在创build操作中:
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)无论你分析闪存哈希,一定要过滤掉闪光[:书]。
– >显示正确的URL,表单数据被保存。 不过,我不知道如何把用户对象放入闪存散列,我不认为这是它的目的。 anyboy是否知道一个更好的地方把它放进去?
由于您正在通过发布到/books/
创build资源,因此它不会将您置于/books/
/books/new
。 当你的创build失败时,只是渲染新的动作,而不是将你redirect到新的动作。 正如@MrYoshiji上面所说的,你可以尝试将它redirect到新的动作,但是这是非常低效的,因为你将创build另一个HTTP请求并往返服务器,只是改变url。 在这一点上,如果它很重要,你可以使用javascript改变它。
它可以通过使用相同的url,但不同的方法来修复新的和创build的行动。
在路由文件中可以使用下面的代码。
resources :books do get :common_path_string, on: :collection, action: :new post :common_path_string, on: :collection, action: :create end
现在你的新页面将呈现在URL
书籍/ common_path_string
如果在validation之后出现任何错误,那么url也是一样的。
也在表单中改为使用
books_path
使用
url: common_path_string_books_path, method: :post
select你喜欢的common_path_string 。
这是我的解决scheme。 不知道它是RESTful,还是Raily和其他任何东西(我仍然是初学者),但至less还在工作:
# routes.rb post 'posts/new', to: 'posts#create', as: 'post_create' resources :posts # posts_controller.rb def new end def create if Post.save? redirect_to posts_path else render action: 'new' end # new.html.erb <%= form_with model: Post.new, url: :post_create, method: :post, local: true do |f| %> <%= f.submit 'Submit' %>
更新
尽pipe解决scheme在技术上是有效的,但考虑到create
行动的目标是创造(在大多数情况下,这是正面的),我将考虑这一点:
除非你真的需要,否则你不应该让浏览器重新打电话,所以当你使用
redirect_to
时候,总是提出问题,如果是正确的,或者render
会更好。
从这篇文章