在Rails 3.1中为单个资源路由多个控制器

时间:2011-09-27 17:23:22

标签: ruby-on-rails ruby-on-rails-3 model-view-controller rails-routing

一周前我问了一个与此类似的问题,但这是一个略有不同的观点。该问题的性质涉及被重定向到正确的控制器。

我有一个资源,帖子,我有4个不同的类别,这些帖子可以在。我希望这些类别中的每一个都特定于单个控制器,因此我在routes.rb中有以下内容:

resources "code", :controller => :code_posts, :as => :code
resources "sports", :controller => :sports_posts, :as => :sports
resources "gaming", :controller => :game_posts, :as => :gaming
resources "the-nation", :controller => :personal_posts, :as => :the_nation

所以现在我可以通过URL访问帖子,例如/ code / 1,/ sports / 34来访问相同的帖子资源,但每个控制器都关注一个范围,即特定的类别。

这一切都很好,但是当我尝试编辑或保存特定帖子时,我的问题出现了。我在其特定控制器的所有视图文件夹中都有以下部分_form.html.erb(在新视图和编辑视图中呈现):

<%= form_for @post do |f| %>
<div class="field">
  <%= f.label :author %><br/>
  <%= f.text_field :author %>
</div>
<div class="field">
    <%= f.label :title %><br/>
    <%= f.text_field :title %>
</div>
<div class="field">
  <%= f.label :category %>
  <%= f.select :category_id, Category.all.collect {|c| [c.name, c.id] }, {:include_blank => true} %>
</div>
<div class="field">
  <%= f.label :summary %><br/>
  <%= f.text_area :summary, :rows => 5 %>
</div>
<div class="field">
  <%= f.label :body %><br/>
  <%= f.text_area :body %>
</div>
<div class="field">
  <%= f.label :tag_tokens %><br/>
  <%= f.text_field :tag_tokens, "data-pre" => @post.tags.map(&:attributes).to_json %>
</div>
<div class="field">
    <%= f.submit "Submit" %>
</div>
<% end %>

因此,每当我创建或更新帖子时,无论通过哪个控制器,我总会被重定向回/ posts / 4,/ posts / 123,/ posts /:id,等等。我想重定向到特定控制器正在编辑或创建的帖子。因此,如果我转到/ code / new,并提交新帖子,我希望被重定向到/ code / 1234,而不是/ posts / 1234。我怎样才能做到这一点?出于某种原因,我今天早上只有主要的精神障碍。感谢。

编辑<%= form_for @post do |f| %>更新为<%= form_for @post, :url => code_url(@post) do |f| %>,它适用于/ code / 1 / edit但不适用于/ code / new。尝试访问新的帖子表单时,出现以下错误:

No route matches {:action=>"show", :controller=>"code_posts", :id=>#<Post id: nil, author: "Les Peabody", summary: nil, body: nil, created_at: nil, updated_at: nil, title: nil, category_id: 1, slug: nil>}

这是我的CodePostsController#new method

def new
  @post = Post.new(:category => Category.find_by_name("Programming"), :author => current_user.full_name)
end

3 个答案:

答案 0 :(得分:0)

我认为原因是form_for方法将更新操作视为默认的参数名称(此处为post)。

所以要改变它,你必须在开头添加(对于示例资源code)以下内容:

<%= form_for @post, :url => code_path(@post) do |f| %>

这当然只是现有对象的URL,新对象的URL应该不同。它应该是new_code_path(并且没有参数)。因此,您的部分应该只包含字段和标签,而不是form_for调用,因为网址应该不同。

您应该在shell中查看调用的输出:bundle exec rake routes并在输出中搜索正确的路径。

答案 1 :(得分:0)

您可以在表单中指定网址

  

&lt;%= form_for @post,:url =&gt; gaming_path做| f | %GT;

您可以在模型上使用继承。 表单中的路径由类名确定,在这种情况下,它是post。 如果他们用资源命名它也应该生成适当的路径。

肮脏的黑客可能会在其中保留物体路径,我看到有人这样做,但我不建议太多。

答案 2 :(得分:0)

因此,最重要的是如何将表单转换为HTML。如果你看一下用于编辑的表单和一个用于新对象的表单之间的差异,那么只有一件事情真正与众不同 - 行动URL。

对于新表单,表单标记应如下所示:

<form accept-charset="UTF-8" action="/code" class="new_post" id="new_post" method="post">

并且在编辑表格的情况下:

<form accept-charset="UTF-8" action="/code/1" class="edit_post" id="edit_post_1" method="post">

然而,对rails来说唯一重要的是输入元素的名称(在两种形式中都是常量)和form标签中的 action 属性。这告诉Rails它是否呈现编辑或创建动作。

由于我们通过多个控制器分割对单个资源的控制,标准form_for @post是不够的,因为Rails不再能够通过约定自动化渲染过程(因为我们正在做一个非常规的事情) 。有必要做一些体力劳动。以下将做到这一点。

将部分转换为以下内容:

<%= form_for @post, :url => path do |f| %>
<div class="field">
  <%= f.label :author %><br/>
  <%= f.text_field :author %>
</div>
<div class="field">
    <%= f.label :title %><br/>
    <%= f.text_field :title %>
</div>
<div class="field">
  <%= f.label :category %>
  <%= f.select :category_id, Category.all.collect {|c| [c.name, c.id] }, {:include_blank => true} %>
</div>
<div class="field">
  <%= f.label :summary %><br/>
  <%= f.text_area :summary, :rows => 5 %>
</div>
<div class="field">
  <%= f.label :body %><br/>
  <%= f.text_area :body %>
</div>
<div class="field">
  <%= f.label :tag_tokens %><br/>
  <%= f.text_field :tag_tokens, "data-pre" => @post.tags.map(&:attributes).to_json %>
</div>
<div class="field">
    <%= f.submit "Submit" %>
</div>
<% end %>

path变量中有一个变量通过部分渲染中的:locals机制传入,如下所示:

<强> new.html.erb

<%= render :partial => "form", :locals => {:path => code_index_path} %>

edit.html.erb

<%= render :partial => "form", :locals => {:path => code_path(@post)} %>

此解决方案的优点是您可以通过在app / views / layouts或app / views / posts中放置 _form.html.erb 来干掉代码,并在所有为所有以一致方式操作Post资源的控制器创建新视图和编辑视图。所以而不是:

<%= render :partial => "form", :locals => {:path => code_path(@post)} %>

我们有:

<%= render :partial => "layouts/form", :locals => {:path => code_path(@post)} %>