避免在Rails视图中出现nil值

5
我相信这个问题已经被问过了,但我找不到答案。
我有一个Project模型,它与我的Client模型有一个belongs_to关系。客户有一个名称,但项目不一定有客户。
在我的视图中,我有如下代码:
<%=h project.client && project.client.name %>

因为如果项目没有客户端,尝试访问project.client.name会导致NoMethodError(nil没有名为name的方法)。

问题是,在视图中使用这种nil检查是否可接受,还是应该寻找其他解决方法?

6个回答

10

只需使用

project.client.try(:name)

我忘记那个了... :) 然而,当你遍历深入 5-6 个模型时,仍然会变得繁琐。:( - DGM
@Tass,你对迪米特法则的理解是正确的,但我认为那不是正确的实现方式,请看下面我的帖子。 - dombesz
@dombesz 但是你的方法会使本地化变得更加困难。如果你想尊重Demeter法则,请发出警告或类似的东西。 - Reactormonk

3
我经常遇到这个问题,是很烦人的。即使永远不应该出现nil,我继承的脏数据有时也会触发它。
你提供的解决方案是一种处理方法。你也可以在Project中添加一个名为client_name的方法,如果存在客户名称,则显示客户名称,但这样就将模型链接在一起,超出了一些人的建议范围。
def client_name
  client && client.name
end

你也可以制作一个帮助方法来完成它,但你可能会写很多这样的方法。 :) 正如下面 Skilldrick 所提到的,添加默认字符串也很有用。
def client_name
  client ? client.name : "no client"
end

1
这在某些情况下绝对是有用的(例如,如果您想要一个默认名称,如“没有客户端”)。 - Skilldrick
2
另一种不使用三元运算符的默认字符串实现方式:client.name ||“无客户” - Eric
当然,有人可能会争辩说,由于最初的问题讨论了拥有纯MVC模型,因此通过在您的模型中放置默认显示字符串,您可能会将更多的视图逻辑注入到您的模型中,对吧?这就是为什么我会像提问者那样去做。但是,这都是风格问题,这种方法也可以运行 :)。我只是不想假设我制作的每个视图都需要相同的默认字符串,你知道吗? - jasonpgignac
有没有一种 gem 或者其他工具可以自动处理这种类型的情况? - BenKoshy

3

我认为这是完全可以接受的 - 这是视图逻辑,您基本上是根据是否有数据来决定是否显示您的视图的部分。


2
您可以在您的Project类中使用delegate,这样您将遵守迪米特法则,该法则指出您应该“只与您的直接朋友交谈”。

project.rb

class Project
  delegate :name, to: :client, prefix: true, allow_nil: true    
end

因此,这样项目对象就知道该在何处询问客户的姓名:
#You can now call
project.client_name

Rails文档中了解更多关于delegate的内容。

0

我的临时解决方案是使用yield块并捕获错误。许多人会说使用rescue作为逻辑非常不好。只要不在需要知道某些东西为空且不应该为空的情况下使用即可。

在application_helper.rb中:

  def none_on_fail
      begin
          return yield
      rescue
          return "(none entered)"
      end
  end

然后在视图中:

<%= none_on_fail { project.client.name }  %>

然后方法可以链式调用,深度可以任意设置,并且它可以用于任何方法,但是如果存在其他潜在的模型/关系/方法问题,它将掩盖这些问题。我会把它比作用火焰喷射器取出刺,如果使用不当,会产生痛苦的后果。


0

我认为这些检查通常可以通过一点思考来消除。这样做的好处是保持您的视图代码更清洁,更重要的是,将逻辑保持在视图层之外,这是最佳实践。有些模板引擎不允许在视图中使用任何逻辑。

至少有几种情况。比如说,您有一个依赖于实例变量的show操作。如果未找到记录,则控制器不应该渲染HTML,而应该通过重定向或其他方式进行处理。如果您在视图中有一个数组的循环,请使用@array.each do |a| end,以便在数组为空时不进行评估。如果您真的想在视图中使用应用程序默认值,请尝试从配置文件中加载它,例如@page_title || #{@APP_CONFIG['page_title']}(请参见Railscasts #85)。请记住,您可能希望稍后更改这些字符串,例如翻译UI。

以下是一些可以避免使用存在性检查和try的情况。如果可能的话,我会尽量避免使用它们。如果无法避免,我会将条件检查放在视图助手中,并添加一个助手单元测试来验证(和记录)两个代码路径。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接