Rails:通过表单创建新记录时验证失败后的URL

时间:2011-03-18 04:00:29

标签: ruby-on-rails ruby-on-rails-3 url view controller

让我们说我正在使用表单和标准的Rails restful控制器创建一个新的Foo,它看起来像这样:

class FoosController < ApplicationController
  ...
  def index
    @foos = Foo.all
  end

  def new
    @foo = Foo.new
  end

  def create
    @foo = Foo.create(params[:foo])
    if @foo.save
      redirect_to foos_path, :notice => 'Created a foo.'
    else
      render 'new'
    end
  end
  ...
end

所以,如果我使用标准的restful控制器(如上所述),那么当我创建Foo时我就在example.com/foos/new,如果我提交表单并且它保存正确我就在{{ 1}}显示索引操作。但是,如果未正确填充表单,则会再次呈现表单并显示错误消息。这都是普通的香草。

但是,如果显示错误,则会呈现表单页面,但URL将为example.com/foos,因为CREATE操作会发布到该URL。但是,人们希望在example.com/foos找到Foos #index,而不是他们刚刚提交的表单,并添加了错误消息。

这似乎是Rails的标准行为,但它对我来说并没有多大意义。显然我可以重定向回new而不是从create action渲染new,但问题是错误消息等会随着内存中部分完整的Foos而丢失。

这个问题是否有一个干净的解决方案,当他们提交的新Foo表单中存在错误时,可以将人们发回example.com/foos

谢谢!

4 个答案:

答案 0 :(得分:9)

回答你对另一个答案的评论:

  

我想知道是否有办法在没有重写控制器的情况下告诉rails你希望URL匹配渲染的模板,而不是调用它的控制器动作。

我不这么认为; URL直接绑定到路由,路由绑定到控制器和操作对 - 渲染层根本不接触它。

要回答您原来的问题,请点击another similar question我的回复信息。


正如您所发现的,默认情况下,当您指定resources :things时,用于创建新内容的POST路径位于/things。这是rake routes的输出:

    things GET    /things(.:format)          {:action=>"index", :controller=>"things"}
           POST   /things(.:format)          {:action=>"create", :controller=>"things"}
 new_thing GET    /things/new(.:format)      {:action=>"new", :controller=>"things"}
edit_thing GET    /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
     thing GET    /things/:id(.:format)      {:action=>"show", :controller=>"things"}
           PUT    /things/:id(.:format)      {:action=>"update", :controller=>"things"}
           DELETE /things/:id(.:format)      {:action=>"destroy", :controller=>"things"}

听起来你想要更像这样的东西:

create_things POST   /things/new(.:format)      {:action=>"create", :controller=>"things"}
       things GET    /things(.:format)          {:action=>"index", :controller=>"things"}
    new_thing GET    /things/new(.:format)      {:action=>"new", :controller=>"things"}
   edit_thing GET    /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
        thing GET    /things/:id(.:format)      {:action=>"show", :controller=>"things"}
              PUT    /things/:id(.:format)      {:action=>"update", :controller=>"things"}
              DELETE /things/:id(.:format)      {:action=>"destroy", :controller=>"things"}

虽然不推荐,但您可以通过以下路线获得此结果:

resources :things, :except => [ :create ] do
  post "create" => "things#create", :as => :create, :path => 'new', :on => :collection
end

您还需要修改表单,使其POST到正确的路径。

答案 1 :(得分:4)

您可以通过在初始化程序中添加它来挂钩rails路由: https://gist.github.com/903411

然后将常规资源放入routes.rb:

resources :users

它应该创建您正在寻找的路线和行为。

答案 2 :(得分:2)

如果您担心要显示的网址,可以手动设置路由。根据您的需要,您可以GET /foos/new呈现您的表单,并使用相同的网址POST进行创建:

map.with_options :controller => :foos do |foo|
    foo.new_foo    '/foos/new', :conditions => {:method => :get},  :action => :new
    foo.create_foo '/foos/new', :conditions => {:method => :post}, :action => :create
    foo.foos       '/foos',     :conditions => {:method => :get},  :action => :index
end

这应该可以在不需要对控制器进行任何更改的情况下工作(耶!) - 您的示例中的所有三个操作都将得到处理。少数免责声明:

  1. 这是基于我对2.3.8应用程序的路由 - 可能需要一些语法(语义?)更改才能使其成为Rails 3路由样式。
  2. 我尝试将这种路由方式与map.resources混合使用已经失败了 - 除非你比我更熟悉这个,或者Rails 3路由更好(两者都很容易),你将不得不这样做这适用于控制器的每条路线。
  3. 最后,不要忘记将/:id(.:format)等添加到需要它们的路由中(本例中没有,但请参阅#2)。
  4. 希望这有帮助!

    修改最后一件事 - 您需要在form_for上的/foos/new.html.erb帮助中对网址进行硬编码。只需添加:url => create_foo_path,因此Rails不会尝试发布到/foos,它默认会发布(可能有一种方法可以更改模型中的创建URL,但我不知道它,如果有的话。)

答案 3 :(得分:1)

您可以使用Rack::Flash在用户的会话中存储您想要的参数,然后重定向到您的表单网址。

def create
  @foo = Foo.new(params[:foo])
  if @foo.save
    redirect_to foos_path, :notice => 'Created a foo.'
  else
    flash[:foo] = params[:foo]
    flash[:errors] = @foo.errors
    redirect_to new_foo_path #sorry - can't remember the Rails convention for this route
  end
end

def new
  # in your view, output the contents of flash[:foo]
  @foo = Foo.new(flash[:foo])
end