我的Rails视图和控制器充斥着 redirect_to
, link_to
和 form_for
方法调用 . 有时 link_to
和 redirect_to
在他们链接的路径中是明确的(例如 link_to 'New Person', new_person_path
),但很多时候路径是隐式的(例如 link_to 'Show', person
) .
我将一些单表继承(STI)添加到我的模型(比如 Employee < Person
),所有这些方法都打破了子类的实例(比如 Employee
);当rails执行 link_to @person
时,它出错 undefined method employee_path' for #<#<Class:0x000001022bcd40>:0x0000010226d038>
. Rails正在寻找由对象的类名定义的路由,即雇员 . 这些员工路线未定义,并且没有员工控制器,因此也未定义操作 .
之前已经问过这个问题:
-
在StackOverflow,答案是在整个代码库中编辑link_to等的每个实例,并明确说明路径
-
再次_1111093_,有两个人建议使用
routes.rb
将子类资源映射到父类(map.resources :employees, :controller => 'people'
) . 在同一个SO问题中的最佳答案建议使用.becomes
在代码库中对每个实例对象进行类型转换 -
还有一个在StackOverflow,最重要的答案是在Do Repeat Yourself阵营,并建议为每个子类创建重复的脚手架 .
在SO处再次提出同样的问题,其中最佳答案似乎是错误的(Rails magic Just Works!) -
在网络的其他地方,我找到了this blog post,其中F2Andy建议在代码中的任何地方编辑路径 .
-
在Logical Reality Design的博客文章Single Table Inheritance and RESTful Routes上,建议将子类的资源映射到超类控制器,如上面的答案编号2所示 .
-
Alex Reisner有一个帖子Single Table Inheritance in Rails,他主张反对将子类的资源映射到
routes.rb
中的父类,因为它只能从link_to
和redirect_to
捕获路由断点,而不是从form_for
捕获 . 所以他建议在父类中添加一个方法,让子类对它们的类撒谎 . 听起来不错,但他的方法给了我错误undefined local variable or method
child' for #` .
因此,似乎最优雅且最具共识的答案(但并非所有优雅,也不是那么多共识),是为您的 routes.rb
添加资源 . 除此之外不适用于 form_for
. 我需要一些清晰度!为了提炼上述选择,我的选择是
-
将子类的资源映射到
routes.rb
中超类的控制器(并希望我不需要在任何子类上调用form_for) -
覆盖rails内部方法,使类彼此相互依赖
-
编辑代码中的每个实例,其中隐式或显式调用对象操作的路径,更改路径或键入对象 .
有了所有这些相互矛盾的答案,我需要一个裁决 . 在我看来,似乎没有好的答案 . 这是rails设计中的失败吗?如果是这样,它是否可以修复?或者如果没有,那么我希望有人可以让我直截了当,让我了解每个选项的优点和缺点(或解释为什么不是一个选项),哪一个是正确的答案,以及为什么 . 或者是否有一个我在网上找不到的正确答案?
14 回答
如果没有嵌套路由,可以试试这个:
或者你可以采用另一种方式并使用如下所述的OOP-magic:https://coderwall.com/p/yijmuq
在第二种方式中,您可以为所有嵌套模型创建类似的帮助程序 .
这是我能够提出的最简单的解决方案,副作用最小 .
现在
url_for @person
将按预期映射到contact_path
.How it works: URL助手依靠
YourModel.model_name
来反映模型并生成(在许多方面)单数/复数路由键 . 这里Person
基本上是说我就像Contact
老兄,问他 .我有同样的问题 . 使用STI后,
form_for
方法发布到错误的子URL .我最后添加了子类的额外路由并将它们指向相同的控制器
另外:
在这种情况下,结构实际上是一个建筑物(子类)
在使用
form_for
提交后,它似乎对我有用 .我建议你看一下:https://stackoverflow.com/a/605172/445908,使用这个方法会让你使用"form_for" .
在路线中使用类型:
http://samurails.com/tutorial/single-table-inheritance-with-rails-4-part-2/
遵循@Prathan Thananart的想法,但试图不破坏任何东西 . (因为涉及太多魔法)
现在url_for @person将按预期映射到contact_path .
我也遇到了这个问题的麻烦,并且通过这个答案来了问题类似于我们的 . 它对我有用 .
答案显示在这里:Using STI path with same controller
.becomes
方法被定义为主要用于解决像form_for
之类的STI问题 ..becomes
info here:http://apidock.com/rails/ActiveRecord/Base/becomes超级迟到的回应,但这是我能找到的最佳答案,对我来说效果很好 . 希望这对某人有所帮助 . 干杯!
好吧,我在Rails的这个领域遇到了很多挫折,并且已经达到了以下方法,也许这将有助于其他人 .
首先要注意的是,网络上方和周围的许多解决方案都建议在客户端提供的参数上使用constantize . 这是一个已知的DoS攻击向量,因为Ruby不会垃圾收集符号,从而允许攻击者创建任意符号并消耗可用内存 .
我已经实现了下面的方法,它支持模型子类的实例化,并且从上面的contantize问题是SAFE . 它与rails 4的功能非常相似,但也允许不止一个级别的子类(与Rails 4不同),并且可以在Rails 3中运行 .
在为开发问题中的“子类加载”尝试了各种方法之后,我发现唯一可行的方法就是在我的模型类中使用'require_dependency' . 这可确保类加载在开发中正常工作,并且不会导致 生产环境 中出现问题 . 在开发中,没有'require_dependency'AR不会知道所有子类,这会影响为类型列匹配而发出的SQL . 此外,如果没有'require_dependency',您最终也会遇到具有多个版本的模型类的情况! (例如,当您更改基类或中间类时,可能会发生这种情况,子类似乎并不总是重新加载,而是从旧类继承子类)
我也没有按照上面的建议覆盖model_name,因为我使用I18n并且需要不同的字符串用于不同子类的属性,例如:tax_identifier变为组织的'ABN',以及Person的'TFN'(在澳大利亚) .
我也使用路由映射,如上所述,设置类型:
除了路由映射,我正在使用InheritedResources和SimpleForm,我使用以下通用表单包装器来执行新操作:
...以及编辑操作:
为了使这项工作,在我的基础ResourceContoller中,我将InheritedResource的resource_request_name公开为视图的辅助方法:
如果您没有使用InheritedResources,请在“ResourceController”中使用以下内容:
总是乐于听到别人的经历和改进 .
我最近documented我尝试在Rails 3.0应用程序中使用稳定的STI模式 . 这是TL; DR版本:
这种方法可以解决您列出的问题以及其他人在STI方法中遇到的其他问题 .
这是一种安全清洁的方式,使其在表单和我们使用的整个应用程序中工作 .
然后我有我的形式 . 增加的部分是as :: district .
希望这可以帮助 .
如果我考虑这样的STI继承:
在'app / models / a_model.rb'中我添加:
然后在AModel类中:
因此,我甚至可以轻松地选择我想使用默认模型的模型,而这甚至没有触及子类定义 . 非常干燥 .
这种方式对我有用(在基类中定义此方法):
您可以创建返回用于路由purpouse的虚拟父对象的方法
然后简单地调用form_for @ employee.routing_object,其中没有类型将返回Person类对象
hackish,但只是解决方案列表中的另一个 .
适用于rails 2.x和3.x.