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

发布于 2024-10-22 18:46:49 字数 927 浏览 3 评论 0原文

假设我正在使用表单和标准 Rails 静态控制器创建一个新的 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

因此,如果我使用标准静态控制器(如上所述),那么当我创建 Foo 时,我位于 < code>example.com/foos/new,如果我提交表单并且正确保存,我将在 example.com/foos 显示索引操作。但是,如果表单未正确填写,则会再次呈现表单并显示错误消息。这都是普通的香草。

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

这似乎是 Rails 标准行为,但对我来说没有多大意义。显然,我可以重定向回 new,而不是从创建操作中渲染 new,但这样做的问题是错误消息等将与内存中部分完整的 Foos 一起丢失。

是否有一个干净的解决方案来解决这个问题,当人们提交的新 Foo 表单出现错误时,将他们送回 example.com/foos/new

谢谢!

Lets say I am creating a new Foo using a form and a standard Rails restful controller, which looks something like this:

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

So, if I use the standard restful controller (as above), then when I'm creating the Foo I am at example.com/foos/new, and if I submit the form and it saves correctly I'm at example.com/foos showing the index action. However, if the form is not filled correctly the form is rendered again and error messages are shown. This is all plain vanilla.

However, if errors are shown, the form page will be rendered but the URL will be example.com/foos, because the CREATE action posts to that url. However, one would expect to find Foos#index at example.com/foos, not the form they just submitted now with error messages added.

This seems to be Rails standard behavior, but it doesn't make a lot of sense to me. Obviously I could redirect back to new instead of rendering new from the create action, but the problem with that is the error messages etc. would be lost along with the partially complete Foos in memory.

Is there a clean solution for this problem, a way to send people back to example.com/foos/new when there are errors in the new Foo form they submitted?

Thanks!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

相思故 2024-10-29 18:46:49

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

我想知道是否有一种方法,无需重写控制器,即可告诉 Rails 您希望 URL 与呈现的模板匹配,而不是与调用它的控制器操作匹配。

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

要回答您原来的问题,请参阅以下信息:我回答了另一个类似的问题


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

    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到正确的道路。

To answer your comment on another answer:

I'm wondering if there's a way, without rewriting the controller at all, to tell rails that you want the URL to match the rendered template, rather than the controller action that called it.

I don't think so; URLs are tied directly to routing, which is tied into a controller and action pair--the rendering layer doesn't touch it at all.

To answer your original question, here's information from another similar question I answered.


As you've found, by default when you specify resources :things, the POST path for creating a new thing is at /things. Here's the output for 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"}

It sounds like you want something more like this:

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"}

Although not recommended, you can get this result with the following route:

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

You would also need to modify your forms to make them POST to the correct path.

厌倦 2024-10-29 18:46:49

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

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

resources :users

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

You could hook into rails routing by adding this in an initializer:
https://gist.github.com/903411

Then just put the regular resources in your routes.rb:

resources :users

It should create the routes and behaviour you are looking for.

空名 2024-10-29 18:46:49

如果您担心要显示的 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)。

希望这有帮助!

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

You can set up the routing manually, if you're that concerned about what URL is going to show. For what you want, you can have a GET to /foos/new render your form, and a POST to the same URL do the creation:

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

This should work without requiring any changes to your controller (yay!) - all three actions from your example are taken care of. The few disclaimers:

  1. This is based on my routing for a 2.3.8 app - some syntax (semantics?) changes are probably required to get it into Rails 3 routing style.
  2. My attempts to mix this style of routing with map.resources have failed horribly - unless you're more familiar with this than me, or Rails 3 routing is better (both easily possible), you'll have to do this for every route to the controller.
  3. And finally, don't forget to add /:id, (.:format), etc. to the routes that need them (none in this example, but see #2).

Hope this helps!

Edit: One last thing - you'll need to hard-code the URL in your form_for helper on /foos/new.html.erb. Just add :url => create_foo_path, so Rails doesn't try to post to /foos, which it will by default (there might be a way to change the creation URL in the model, but I don't know of it, if there is one).

記柔刀 2024-10-29 18:46:49

您可以使用 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

You could use Rack::Flash to store the parameters you wanted in the user's session and then redirect to your form 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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文