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

发布于 2024-12-07 09:23:51 字数 2420 浏览 4 评论 0原文

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

我有一个资源,帖子,这些帖子可以分为 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) 做 |f| %> 它适用于 /code/1/edit 但不适用于 /code/new。尝试访问新的帖子表单时,出现以下错误:

没有路线匹配 {:action=>"show", :controller=>"code_posts", :id=>#}

这是我的 CodePostsController#new 方法

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

I asked a question similar to this one about a week ago, but this is a slightly different perspective on it. The nature of the question involves being redirected to the correct controller.

I have a single resource, posts, and I have 4 different categories these posts can be under. I want each of these categories to be particular to a single controllers, and so I have the following in my 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

So now I can access posts through URLs like, for example, /code/1, /sports/34 to access the same post resource, but with each controller focusing on a single scope, namely a particular category.

This is all well and good, but my issue comes up when I try to edit or save particular posts. I have the following partial _form.html.erb (rendered in the new and edit views) in all the view folders for their particular controller:

<%= 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 %>

So whenever I create or update a post, through whichever controllers, I always get redirected back to /posts/4, /posts/123, /posts/:id, whatever. I want to get redirected to the particular controller the post being edited or created lives under. So if I go to /code/new, and submit the new post, I want to be redirected to /code/1234, and not /posts/1234. How can I do this? For some reason I'm just having major mental mind blocks this morning. Thanks.

EDIT Updated <%= form_for @post do |f| %> to <%= form_for @post, :url => code_url(@post) do |f| %> and it works for /code/1/edit but not /code/new. When trying to access a new post form, I get the following error:

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

This is my CodePostsController#new method

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

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

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

发布评论

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

评论(3

掌心的温暖 2024-12-14 09:23:52

因此,最终重要的是如何将表单转换为 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 来说唯一重要的是输入元素的名称(在两种表单中都是常量)和表单标签中的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)} %>

这个解决方案的好处是,您也可以通过将 _form.html.erb 放在 app/views/layouts 或 app/views 中来干燥代码/posts 并在所有新内容中重用它并编辑以一致方式操作 Post 资源的所有控制器的视图。因此,我们没有:

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

我们有:

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

So, ultimately what is important is how the form gets turned into HTML. If you look at the differences between a form that is meant for editing, and a form that is meant for a new object, there is only one thing that is ever really different that matters - the action URL.

In the case of a new form, the form tag should look something like:

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

and in the case of an edit form:

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

The only thing that matters to rails however is the name of the input elements (which are constant in both forms) and the action attribute in the form tag. That tells Rails whether or not it's rendering the edit or create action.

Since we're splitting up control of a single resource through multiple controllers, the standard form_for @post will not suffice since Rails can no longer automate the rendering process through convention (as we're doing a very unconventional thing). It is necessary to do some manual labor. The following will do the trick.

Convert the partial to the following:

<%= 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 %>

The path variable in there is a variable passed in through the :locals mechanism in the partial render, like so:

new.html.erb

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

and edit.html.erb

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

The nice thing with this solution is you can DRY up the code too by placing _form.html.erb in app/views/layouts or app/views/posts and reuse it in all of the new and edit views for all controllers that manipulate the Post resource in a consistent fashion. So rather than having:

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

we have:

<%= render :partial => "layouts/form", :locals => {:path => code_path(@post)} %>
惜醉颜 2024-12-14 09:23:51

您可以在表单中指定 url

<%= form_for @post, :url => games_path 做 |f| %>

您可以在模型上使用继承。
表单中的路径由类名决定,在本例中为 post。
如果它们与资源命名匹配,它也应该生成正确的路径。

肮脏的黑客可能会在其中保留对象路径,我看到有人这样做,但我不太推荐它。

You may specify the url in the form

<%= form_for @post, :url => gaming_path do |f| %>

You may use inheritance on the model.
The path in you form is determined by the class name, and in this case it is post.
If they mach with resources naming it should generate proper paths as well.

The dirty hack may be keeping objects path in it, I saw someone do that, but I do not recommend it too much.

清君侧 2024-12-14 09:23:51

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

因此,要改变这一点,您必须在开头(对于示例资源 code)添加以下内容:

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

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

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

I think the reason is the form_for method which takes for the update action as default the name of the parameter (here post) it gets.

So to change that, you have to add at the beginning (for the example resource code) the following:

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

This is of course only the URL for an existing object, the URL for a new object should be different. It should be there new_code_path (and no argument). So your partial should only contain the fields and labels, not the form_for call, because the URL should be different then.

You should look at the output of the call in the shell: bundle exec rake routes and search for the correct paths in the output.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文