Rails的:通过形式创造新的记录时

Rails的:通过形式创造新的记录时

问题描述:

比方说,我使用的是形式和标准的Rails宁静控制器,它看起来像这样创建一个新的Foo确认后网址失败:Rails的:通过形式创造新的记录时

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 

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

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

这似乎是Rails的标准行为,但它没有很多道理给我。显然,我可以重定向到新的而不是从创建操作中渲染新的,但与此相关的问题是错误消息等将随着部分完整的Foos在内存中丢失。

对于这个问题,是否有一个干净的解决方案,当他们提交的新Foo表单中有错误时,这种方式可以将人员送回example.com/foos/new

谢谢!

你可以挂接到铁轨通过在初始化添加此路由: https://gist.github.com/903411

然后只要把经常资源在你的routes.rb:

resources :users 

应该制定的路线和您的行为正在找。

+0

这实际上是非常有用的,因为虽然它是一种戏剧性的黑客攻击,但它确实开放了测试配置rails应用程序的不同方法的可能性,我可能以后会制作一个gem,甚至提交考虑到rails core 。我喜欢这个想法,我会在稍后尝试。谢谢! – Andrew 2011-04-07 03:26:49

+2

非常有用 - 但:是否有任何理由更新操作不使用编辑路径? – 2011-11-02 20:23:04

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

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 
+0

那么,我猜想存储这些会话是可行的,但我真正想知道的是,如果有一种方法可以更改此默认导轨行为以显示要呈现的布局的url,而不是返回给您的操作到那个布局... – Andrew 2011-03-23 20:37:23

+0

但这就是这样做。它会重定向,然后用闪光灯的内容填充视图。 – stef 2011-03-23 22:43:41

+0

我的意思是,我想知道是否有办法,根本不重写控制器,告诉rails你想让URL匹配渲染模板,而不是调用它的控制器动作。那有意义吗? – Andrew 2011-03-23 23:29:48

如果您关心哪些URL将显示,您可以手动设置路由。你想要的东西,你可以有一个GET/foos/new渲染你的形式,和POST相同的URL做创作:

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)。

希望这有助于!

编辑:最后一两件事 - 你需要硬编码的URL在form_for帮手上/foos/new.html.erb。只需添加:url => create_foo_path即可,因此Rails不会尝试发布到/foos,默认情况下(可能会更改模型中的创建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到正确的路径。

+3

好吧,这几乎可以回答我的问题(不,这并不是一个很好的干净方式)。真正让我困扰的是,当你的新纪录未通过验证时,你所得到的URL不是正在查看的页面的正确URL。如果您重新加载该页面,则不会获得相同的页面。如果你问我,这看起来像是一个糟糕的内部设计决定,但是哦。因为我刚刚获得奖励,所以我会稍微延长一段时间 - 以防其他人有任何其他想法。谢谢! – Andrew 2011-04-03 03:33:20

+1

可以理解。我支持网址很重要的想法,我可以理解这种沮丧。我想知道这个选择背后是否有一些推理。但是,请记住,这不是真正的REST。有一个名为[RESTful Best Practices](http://www.slideshare.net/calamitas/restful-best-practices)的幻灯片,这当然让我想到了。 – 2011-04-03 03:56:44

+0

不幸的是,这个解决方案没有验证错误。 – WojciechKo 2015-08-29 23:42:07