为什么这总是返回 true?Rails

3
def follows(follower, followed)
follow = Follows.where("follower = ? AND followed = ?", follower, followed)
if follow
    true
  else 
    false
  end
end

这是我的视图代码:

<% if current_user.id == @user.id%>
  <p>This is you!</p>
<% else %>
  <% if follows(current_user.id, @user.id)%>
    <p>You already follow <%= @user.username %>
  <% else %>
    <p><%= link_to "Follow!", follow_path(@user.id) %></p>
   <% end %>
<% end %>

我想检查一个用户是否关注另一个用户,所以写了这个代码。它接收两个用户id,并查询数据库,应该在找到匹配项时返回true,否则返回false。但它总是返回true。这是为什么呢?


你肯定得看 Follows.where 方法吧? - Blundell
1
如果函数接受 ids,则参数名称应为 follower_id 和 followed_id。保留 follower 和 followed 用于实际实例,而不仅仅是 ids。 - kikito
5个回答

7
让我们从一些样式和设计问题开始,并以实际答案告终:
  1. 按照惯例,模型是单数。否则只会让你更加辛苦。在这种情况下,我建议将"Following"作为一个合适的名称,例如"a user has many followings"。

  2. 外键应以"_id"结尾。否则只会让你更加辛苦。因此是"follower_id"和"followed_id"。

  3. 被用于真/假性质 ("查询方法") 的方法应以?结尾,因此是"follows?"而不是"follows",

  4. 你的if语句是多余的,一旦条件做正确的事情,它可以安全地删除。在Ruby中,在条件语句的上下文中,我们更关心事物是否求值为真/假,而不是它们是否字面上为true/false。这意味着除了nil或false之外的任何东西都将是"truthy"。

  5. 你的方法完全依赖于已知的信息与User对象相关,这表明最好将它悬挂在这些对象上,例如current_user.follows? other_user

  6. 你在重复使用使用关联就可以提供给你的行为。

最后,考虑到所有这些因素,答案是:
class User < ActiveRecord::Base
  has_many :followings, :class_name => 'Following', :foreign_key => 'followed_id'
  has_many :followers, :through => 'followings'

  def follows?(other)
    other.followed_by? self
  end

  def followed_by?(other)
    followers.include? other
  end
end
NB:这里使用followed_by?方法是使用了双重分派,避免了一个用户直接了解另一个用户的粉丝状态所带来的(轻微的)迪米特法则冲突。相反,第一个用户对象向第二个用户对象直接提问(“你被我关注吗?”),并根据答案得出结果。(它本身也很可能是一个有用的方法。)

非常感谢您向我展示了正确的做法,而不仅仅是一个快速修复! - Mark Provan
多么周到和完整的答案啊。真希望我能给你超过一个赞。 - Rob Di Marco

3
原因是即使没有找到记录,where()方法仍会返回一个空数组。而空数组在逻辑判断中被视为“真”。另外,以下是相关结构:
if (condition)
  true
else
  false
end

可以被替代为:

condition

1

follow实际上是ActiveRecord :: Relation的一个实例,而不是查询结果集。要确定查询是否返回任何行,请使用follow.count。例如。

if follow.count > 0
  true
else 
  false
end

除非你在块中做其他事情,否则请省略true else false end部分。 - DGM

1

你可以使用present?。你的代码应该是

  if follow.present?
    true
  else 
    false
  end

除非您在块中执行其他操作,否则请省略true、else、false和end部分。 - DGM

0

@rein Heinrichs的回答非常棒。他给出了最好的Rails解决方案。但我想解释一下为什么你写的代码不起作用,以及你应该如何修复它。

Follows.where(...)

返回一个数组,验证这一点的简单方法是在Rails控制台中运行该行(在控制台中键入rails c)。 即使是空数组,也不是nil,并且始终会计算为true

因此,要根据是否找到任何关注者来返回布尔值,只需检查where结果中的项目数量(使用size > 0present?

因此,您的follows函数可以重写为:

def follows(follower, followed)
  Follows.where("follower = ? AND followed = ?", follower, followed).present?
end

而且这实际上也很易读。希望这能帮到你。


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