60

我有一个名为 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 数据以及错误消息都会丢失,这就是拥有表单和操作等的全部意义所在。

为什么 render :new 会在我的索引操作 /books 登陆我,而通常该 URL 会调用列出所有书籍的 INDEX 方法?

4

5 回答 5

30

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

于 2013-01-23T23:20:24.590 回答
7

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

新的.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 %>
于 2014-01-02T11:27:10.987 回答
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 hash,一定要过滤掉flash[:book]。

--> 显示正确的 URL,保留表单数据。尽管如此,我还是不喜欢将用户对象放入闪存哈希中,我认为这不是它的目的。有没有人知道一个更好的地方放它?

于 2013-11-28T15:32:17.533 回答
2

它不会让您登陆,/books/new因为您通过发布到创建资源/books/。当您的创建失败时,它只是呈现新操作,而不是将您重定向到新操作。正如@MrYoshiji 上面所说,您可以尝试将其重定向到新操作,但这确实效率低下,因为您将创建另一个 HTTP 请求并往返于服务器,只是为了更改 url。那时,如果它很重要,您可能可以使用 javascript 更改它。

于 2013-01-24T03:43:01.393 回答
1

它可以通过使用相同的 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

如果验证后出现任何错误,url 仍然是相同的。

也在表格中,而不是使用

books_path

利用

url: common_path_string_books_path, method: :post

选择您喜欢的common_path_string

于 2016-09-22T13:21:30.770 回答