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

问题描述 投票:58回答:5

我有一个名为Books的资源。它在我的路由文件中正确地列为资源。

我有一个新操作,它为新视图提供了标准:

@book = Book.new

在模型上,有一些属性可以通过在场验证,因此,如果保存操作失败,将产生错误。

在我的控制器中:

@book = Book.create
...  # some logic
if @book.save
  redirect_to(@book)
else
  render :new
end

这是非常标准的;并且使用render:new的原理是这样,以便将对象传递回视图,并可以报告错误,重新填写表单项,等等。

这有效,除了每次我通过render: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数据以及错误消息将丢失,这是具有表单和操作等的全部内容。

为什么render:new使我进入/ books,我的索引操作,通常该URL调用INDEX方法,该方法列出所有书籍?

ruby-on-rails forms view controller routes
5个回答
28
投票

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


8
投票

我刚开始使用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 %>

4
投票

只是有一个非常相同的问题,所以也许有一天某人会有所帮助。为了使此功能正常运行,您基本上必须进行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,保留表单数据。尽管如此,我还是不喜欢将用户对象放入闪存哈希中,我不认为这是目的。有没有男孩知道更好的放置位置?


2
投票

由于您通过发布到/books/new来创建资源,因此它不会降落在/books/上。当创建失败时,它只是呈现新动作,而不是将您重定向到新动作。就像@MrYoshiji在上面说的那样,您可以尝试将其重定向到新操作,但这确实效率不高,因为您将创建另一个HTTP请求并往返于服务器,仅更改URL。到那时,如果很重要,您可以使用javascript对其进行更改。


1
投票

可以通过使用相同的url,但是使用不同的new和create动作方法来修复它。

在路由文件中,可以使用以下代码。

resources :books do
  get :common_path_string, on: :collection, action: :new
  post :common_path_string, on: :collection, action: :create
end

现在,您的新页面将在URL呈现

books / common_path_string

如果在验证后出现任何错误,则URL仍将相同。

也以表格的形式使用

books_path

使用

url: common_path_string_books_path, method: :post

选择您喜欢的common_path_string

© www.soinside.com 2019 - 2024. All rights reserved.