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

2admgd59  于 2022-11-19  发布在  Ruby
关注(0)|答案(6)|浏览(161)

我有一个资源叫图书。它在我的路线文件中被正确地列为资源。
我有一个新的行动,它赋予了新的观点以标准:

@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方法,列出所有的书?

6yjfywim

6yjfywim1#

实际上,它 * 是 * 把你带到创建路径。它在create操作中,路径是/books,使用HTTP方法POST。这看起来和索引路径/books一样,但是索引路径使用HTTP方法GET。Rails路由代码在决定调用哪个操作时考虑该方法。验证失败后,你仍然在创建操作中,但是呈现的是new视图,这有点混乱,但是像render :new这样的行实际上根本不调用新操作;它仍然在运行create操作,并告诉Rails呈现新的 view

np8igboo

np8igboo2#

我刚开始使用Rails-Tutorial,也遇到了同样的问题。解决方案很简单:如果您在提交表单(有错误)后需要相同的URL,只需将新建和创建操作合并到一个操作中即可。
这是我的代码的一部分,它使这成为可能(希望它能帮助一些人^^)
routes.rb(为new-action添加后路由):

...
    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 %>
ukxgm1gy

ukxgm1gy3#

只是有同样的问题,所以也许有一天这可能会对某人有所帮助。你基本上要做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,保存表单数据。不过,我不喜欢把用户对象放到flash哈希中,我认为这不是它的目的。有没有人知道更好的地方把它放进去?

x7rlezfr

x7rlezfr4#

它不会让你到达/books/new,因为你是通过发布到/books/来创建资源的。当你的创建失败时,它只是呈现新的操作,而不是重定向到新的操作。正如@MrYoshiji上面所说,你可以尝试重定向到新的操作,但这真的很低效,因为你会创建另一个HTTP请求并往返于服务器。只是为了改变url。在这一点上,如果它有关系,你可能会使用javascript改变它。

e7arh2l6

e7arh2l65#

它可以通过使用相同的网址,但不同的方法为新的和创建行动修复。
在路由文件中,可以使用以下代码。

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

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

书籍/公共路径字符串

如果验证后出现任何错误,URL仍将相同。
也在表单中使用

books_path

使用了

url: common_path_string_books_path, method: :post

选择您喜欢的 common_path_string

o8x7eapl

o8x7eapl6#

如果客户端验证失败,您仍然可以在呈现新视图时输入所有字段。服务器端错误沿着会输出到客户端。重新提交时,仍会运行创建操作。

在books_controller.rb中

def new
  @book = current_user.books.build
end

def create
  # you will need to have book_params in private
  @book = current_user.books.build(book_params)
  
  if @book.save
    redirect_to edit_book_path(@book), notice: "Book has been added successfully"
    # render edit but you can redirect to dashboard path or root path
  else
    redirect_to request.referrer, flash: { error: @book.errors.full_messages.join(", ") }
  end
end

在新的.html.erb中

<%= form_for @book, html: {class: 'some-class'} do |f| %>
  ...
  # Book fields
  # Can also customize client side validation by adding novalidate:true, 
  # and make your own JS validations with error outputs for each field. 
  # In the form or use browser default validation.
<% end %>

相关问题