可以在模型中使用Rails路由帮助器(即mymodel_path(model))吗?


368

假设我有一个名为Thing的Rails模型。事物具有url属性,可以选择将其设置为Internet上某个地方的URL。在视图代码中,我需要执行以下操作的逻辑:

<% if thing.url.blank? %>
<%= link_to('Text', thing_path(thing)) %>
<% else %>
<%= link_to('Text', thing.url) %>
<% end %>

视图中的这种条件逻辑是丑陋的。当然,我可以构建一个辅助函数,该函数会将视图更改为:

<%= thing_link('Text', thing) %>

这解决了冗长性问题,但是我真的更希望在模型本身中具有功能。在这种情况下,视图代码将是:

<%= link_to('Text', thing.link) %>

显然,这将需要模型上的链接方法。这是需要包含的内容:

def link
  (self.url.blank?) ? thing_path(self) : self.url
end

问题的关键是,thing_path()是模型代码中未定义的方法。我假设可以将一些辅助方法“拉入”模型,但是如何?并有真正的理由认为路由仅在控制器上运行并查看应用程序层吗?我可以想到很多情况下,模型代码可能需要处理URL(与外部系统集成)。


一个用例是:在一次保存后从goo.gl生成缩短的网址,
lulalala 2012年

2
如果您想添加视图逻辑,则应该将模型包装在演示器中,这样可以使MVC层保持分离。参见Draper(github.com/jcasimir/draper)。
克里斯(Kris)2012年

Answers:


698

在Rails 3、4和5中,您可以使用:

Rails.application.routes.url_helpers

例如

Rails.application.routes.url_helpers.posts_path
Rails.application.routes.url_helpers.posts_url(:host => "example.com")

14
Ypu可以将其包含在任何模块中,之后您可以访问那里的url-helpers。Thx
Viacheslav Molokov

41
避免将您的:host选项复制到任何地方,并在您的环境配置文件中进行一次设置:Rails.application.routes.default_url_options[:host] = 'localhost:3000'
Andrew

5
include Rails.application.routes.url_helpers在Rails 4.1中为我工作
1

1
如果只需要路径,则可以使用:only_path => true作为选项,而无需传递host参数。
trueinViso

1
这似乎是不错的选择:hawkins.io/2012/03/...
Renars Sirotins

182

我已经找到了有关如何自己执行此操作的答案。在模型代码中,只需输入:

对于Rails <= 2:

include ActionController::UrlWriter

对于Rails 3:

include Rails.application.routes.url_helpers

这神奇地使它thing_path(self)返回当前事物的URL,或other_model_path(self.association_to_other_model)返回其他URL。


27
由于不推荐使用上述内容,因此仅作更新。当前包括:include Rails.application.routes.url_helpers
yalestar

2
我尝试执行此操作时收到NoMethodError(#<ActionView :: Base:0x007fe8c0eecbd0>的未定义方法“ optimize_routes_generation?”)
moger777,2015年

118

您可能还会发现以下方法比包含所有方法更干净:

class Thing
  delegate :url_helpers, to: 'Rails.application.routes' 

  def url
    url_helpers.thing_path(self)
  end
end

7
关于此文档的文档似乎很难找到,因此这里是有关ActiveSupport中委托使用的一个不错的链接。simonecarletti.com/blog/2009/12/inside-ruby-on-rails-delegate
tlbrack 2011年

2
我收到undefined local variable or method 'url_helpers' for Event:Class错误消息... :(
Augustin Riedinger 2014年

我懂了undefined method url_helpers。我该怎么办?
asiniy 2015年

@asiniy,确定您已在类声明后编写的url_helpers中使用委托选项吗?
Krzysztof Witczak

无效,但可能只有在创建自己的时才有效class,如答案所示。如果您的Model类已经扩展了,< ApplicationRecord那将不起作用?
skplunkerin

13

与视图中显示内容有关的任何逻辑都应委托给助手方法,因为模型中的方法严格用于处理数据。

您可以执行以下操作:

# In the helper...

def link_to_thing(text, thing)
  (thing.url?) ? link_to(text, thing_path(thing)) : link_to(text, thing.url)
end

# In the view...

<%= link_to_thing("text", @thing) %>

1
在这种情况下,我对辅助方法不满意:当我查看link_to_thing()时,我必须确定这是特定于事物的助手还是整个应用程序范围的辅助器(很容易是其中之一)。我需要考虑检查2个文件的来源。something.link对源文件毫无疑问。
亚伦·朗威尔

另外,如果我需要在Rake任务中使用此功能(例如,导出事物URL的CSV文件)...在这种情况下,直接转到模型也会更好。
亚伦·朗威尔

但是区别在于,link_to在模型中不可用,因为它是一个ActionView帮助器。因此,这将不起作用。您可以修改模型中的某些属性默认值,因此,如果未设置该属性默认值,则默认值为某项,但这取决于您。
乔什·德尔斯曼

12
随着Web服务API的增长,很多时候模型需要使用自己的数据联系外部资源并提供回调URL。例如,一个照片对象需要将自己发布到Socialmod,当执行审核时,该对象将回调到该照片的URL。使用after_create()挂钩可以发布到Socialmod,但是您如何知道回调URL?我认为作者自己的回答很有道理。
JasonSmith

3
从严格的技术角度来说,您是正确的,但有时候人们只需要工作而不必刮every,这是为了避免违反某些神圣的设计模式或您所拥有的东西,也许他们需要获取url,并进行实际存储然后将其存储在数据库中,对于模型仅知道如何做到这一点,而不是来回传递东西,有时可能会违反规则会很方便。
nitecoder 2012年


1

尽管可能有一种方法,但我倾向于将这种逻辑排除在模型之外。我同意您不应该将其放在视图中(保持它的皮包骨头),但是除非模型将URL作为数据返回给控制器,否则路由内容应该在控制器中。


4
回复:“除非模型将URL作为数据返回”。这就是这里正在发生的事情……模型“拥有”这部分数据,该数据是指向现场URL或现场URL的链接。在某些情况下,URL是从Rails路由生成的。在其他情况下,URL是用户提供的数据。
亚伦·朗威尔

0

(编辑:忘记我以前的胡话...)

好的,在某些情况下,您可能会去模型或其他URL……但是我并不是真的认为这属于模型,视图(或模型)听起来更合适。

关于路由,据我所知,路由是针对控制器中的动作的(通常“神奇地”使用视图),而不是直接针对视图。控制器应处理所有请求,视图应显示结果,模型应处理数据并将其提供给视图或控制器。我在这里听到很多人在谈论到模型的路由(以至于我几乎开始不喜欢它),但是据我所知:路由到了控制器。当然,许多控制器是一种模型的控制器,通常被称为<modelname>sController(例如,“ UsersController”是模型“ User”的控制器)。

如果发现自己在视图中编写了令人讨厌的逻辑,请尝试将逻辑移到更合适的位置;请求和内部通信逻辑可能属于控制器,与数据相关的逻辑可以放置在模型中(但不包括显示逻辑,包括链接标签等),而与显示完全相关的逻辑可以放置在帮助器中。


假设您有一个图像模型。如果图像具有关联的外部URL,则指向该URL。否则,指向图片的显示页面上传图片?只是一个主意。
Josh Delsman

这个不太普通的示例怎么样:link_to“全尺寸图片”,image.link模型中的链接方法要么链接到现场URL(image_path),要么链接到Flickr URL(如果用户提供了一个), .url已设置在图像上。
亚伦朗威尔
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.