在Rails 3中,当资源创建操作失败并调用render:new时,为什么URL必须更改为资源的索引URL?

rcd*_*rcd 57 forms controller routes ruby-on-rails view

我有一个名为Books的资源.它在我的路线文件中正确列为资源.

我有一个新动作,它给新视图标准:

@book = Book.new
Run Code Online (Sandbox Code Playgroud)

在模型上,有一些属性通过在线验证,因此如果保存操作失败,将生成错误.

在我的控制器中:

@book = Book.create
...  # some logic
if @book.save
  redirect_to(@book)
else
  render :new
end
Run Code Online (Sandbox Code Playgroud)

这是非常标准的; 以及使用render的基本原理:new是为了将对象传递回视图,并且可以报告错误,重新填充表单,等等.

这是有效的,除了每次我发送回表单(通过渲染:新),我的错误显示,但我的URL是INDEX URL,这是

/books
Run Code Online (Sandbox Code Playgroud)

而不是

/books/new
Run Code Online (Sandbox Code Playgroud)

这是我从一开始就开始的地方.我看过其他几个关于这个问题的帖子,但没有答案.至少,人们会认为它会落在/ books/create,我也有一个视图文件(在这种情况下与new相同).

我可以做这个:

# if the book isn't saved then
flash[:error] = "Errors!"
redirect_to new_book_path
Run Code Online (Sandbox Code Playgroud)

但随后@book数据以及错误消息将丢失,这是表单和操作等的全部要点.

为什么渲染:新的登陆我的/ books,我的索引动作,通常该URL调用INDEX方法,列出所有书籍?

Jim*_*art 27

它实际上将您发送到创建路径.它位于create动作中,其路径是/books使用HTTP方法POST.这看起来与索引路径相同/books,但索引路径使用HTTP方法GET.在确定要调用的操作时,rails路由代码会考虑该方法.验证失败后,您仍处于创建操作中,但您正在渲染new视图.这有点令人困惑,但是一条线路render :new根本就没有实际调用新的动作; 它仍在运行create动作,它告诉Rails渲染新视图.

  • 有没有办法渲染:new,以便将错误传递给表单,同时登录到URL中的/ books/new?考虑到/ books/new是我开始的地方,以及用户期望的地方,似乎看起来会更好并且更有意义. (4认同)
  • 要做到这一点,你必须重定向(参见上面的@ MrYoshiji的评论).提交表单时,浏览器会向`create_book_path`发出请求.除非您随后重定向到另一个页面,否则浏览器仍然认为它位于创建URL.然而,有一些工作要使它真正成为RESTful; 如果你只是使用GET重定向到`new_book_path`,你将不得不在URL中编码表单参数(并不总是理想的,有时不可能使用更复杂/更大的表单数据).RESTful方式是为`/ books/new`添加POST路由以填充模型/表单数据. (2认同)

ofh*_*use 8

我刚开始使用Rails-Tutorial并遇到了同样的问题.解决方案很简单:如果在提交表单(有错误)后需要相同的URL,只需在一个操作中组合new和create操作即可.

这是我的代码的一部分,这使得这成为可能(希望它有助于某人^^)

routes.rb(为new-action添加post-route):

...
    resources :books
    post "books/new"
...
Run Code Online (Sandbox Code Playgroud)

控制器:

...
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
Run Code Online (Sandbox Code Playgroud)

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 %>
Run Code Online (Sandbox Code Playgroud)