Ruby on Rails:单一资源和 form_for

发布于 2024-09-19 21:47:05 字数 407 浏览 6 评论 0原文

我希望用户仅处理连接到用户会话的一个订单。所以我为订单routes.rb设置了单一资源

resource :order

views/orders/new.html.erb:

<%= form_for @order do |f| %>
   ...
<% end %>

但是当我打开新订单页面时,我收到一个错误:

undefined method `orders_path`

我知道,我可以设置 :url => order_pathform_for 中,但是解决这种冲突的真正方法是什么?

I want user to work with only one order connected to user's session. So I set singular resource for order

routes.rb:

resource :order

views/orders/new.html.erb:

<%= form_for @order do |f| %>
   ...
<% end %>

But when I open the new order page I get an error:

undefined method `orders_path`

I know, that I can set :url => order_path in form_for, but what is the true way of resolving this collision?

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

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

发布评论

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

评论(2

江湖彼岸 2024-09-26 21:47:05

不幸的是,这是一个错误。您必须像您提到的那样设置网址。

= form_for @order, :url => order_path do |f|

请注意,这将正确路由到 createupdate,具体取决于 @order 是否持久。


更新

现在还有另一个选择。您可以将其添加到您的路由配置中:

resolve("Order") { [:order] }

然后,当为 polymorphic_url 方法提供一个类名为“Order”的对象时,它将使用 [:order] 作为 url 组件如jskol的回答中所述调用model_name.route_key

这有一个限制,即它不能在范围或命名空间内使用。您可以在路由配置的顶层路由命名空间模型:

resolve('Shop::Order') { [:shop, :order] }

但它不会对具有额外组件的路由产生影响,因此

url_for(@order)           # resolves to shop_order_url(@order)
url_for([:admin, @order]) # resolves to admin_shop_orders_url(@order) 
                          #        note plural 'orders' ↑
                          #        also, 'shop' is from module name, not `resolve` in the second case

Unfortunately, this is a bug. You'll have to set the url like you mention.

= form_for @order, :url => order_path do |f|

Note that this will properly route to create or update depending on whether @order is persisted.


Update

There's another option now. You can add this to your routes config:

resolve("Order") { [:order] }

Then when the polymorphic_url method is given an object with class name "Order" it will use [:order] as the url component instead of calling model_name.route_key as described in jskol's answer.

This has the limitation that it cannot be used within scopes or namespaces. You can route a namespaced model at the top level of the routes config:

resolve('Shop::Order') { [:shop, :order] }

But it won't have an effect on routes with extra components, so

url_for(@order)           # resolves to shop_order_url(@order)
url_for([:admin, @order]) # resolves to admin_shop_orders_url(@order) 
                          #        note plural 'orders' ↑
                          #        also, 'shop' is from module name, not `resolve` in the second case
ぇ气 2024-09-26 21:47:05

这条神奇的道路从何而来?

我进行了大量的跟踪,但最终发现 url_for 使用 ActionDispatch::Routing::PolymorphicRoutes 中定义的 polymorphic_path 方法确定模型的路径。 polymorphic_path 最终通过调用以下内容来获取模型的自动路径:

record.class.model_name.route_key

我稍微简化了一点,但这基本上就是这样。如果您有一个数组(例如 form_for[@order, @item]),则会在每个元素上调用上述方法,并将结果与​​ _ 连接起来。

类上的 model_name 方法来自 ActiveRecord::Naming

module ActiveModel
  ...
  module Naming
    ...
    def model_name
      @_model_name ||= begin
        namespace = self.parents.detect do |n|
          n.respond_to?(:use_relative_model_naming?) && 
                                                 n.use_relative_model_naming?
        end
        ActiveModel::Name.new(self, namespace)
      end
    end
  end
end

我该如何更改它?

幸运的是,ActiveModel::Name 预先计算了包括route_key 在内的所有值,因此要覆盖该值,我们所要做的就是更改实例变量的值。

对于您问题中的 :order 资源:

class Order < ActiveRecord::Base
  model_name.instance_variable_set(:@route_key, 'order')
  ...
end

# new.html.erb
<%= form_for @order do |f| # Works with action="/order" %>
    ...
<% end %>

尝试一下!

Where does that magic path come from?

It took me a lot of tracing but I ultimately found that the url_for determines the path for your model using the polymorphic_path method defined in ActionDispatch::Routing::PolymorphicRoutes. polymorphic_path ultimately gets the automagic path for your model by calling something along the lines of:

record.class.model_name.route_key

I'm simplifying slightly but this is basically it. If you have an array (e.g. form_for[@order, @item]) the above is called on each element and the results are joined with _.

The model_name method on your Class comes from ActiveRecord::Naming.

module ActiveModel
  ...
  module Naming
    ...
    def model_name
      @_model_name ||= begin
        namespace = self.parents.detect do |n|
          n.respond_to?(:use_relative_model_naming?) && 
                                                 n.use_relative_model_naming?
        end
        ActiveModel::Name.new(self, namespace)
      end
    end
  end
end

How can I change it?

Fortunately ActiveModel::Name precalculates all values including route_key, so to override that value all we have to do is change the value of the instance variable.

For the :order resource in your question:

class Order < ActiveRecord::Base
  model_name.instance_variable_set(:@route_key, 'order')
  ...
end

# new.html.erb
<%= form_for @order do |f| # Works with action="/order" %>
    ...
<% end %>

Try it out!

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