查找用户是否已经标记了一个对象

Finding if a user has already flagged an object

我目前设置了一个实例,其中 Review 有很多 Flag(通过多态性),User 有很多标志。到目前为止一切正常,但我现在想在视图中显示一条消息,如果用户标记了特定评论,Flag Submitted 而不是 link标记它。

现在,我首先收集属于评论的所有标记,然后使用 any? 确定其中是否有当前用户的 ID。我想知道是否有 faster/more 有效的方法来做到这一点?是查找链让我全面了解它。

注意: flaggable 是一个局部变量,在本例中代表一个 @review

<%= render "flag", flaggable: @review %>

class Review < ActiveRecord::Base
  has_many :flags, as: :flaggable, dependent: :destroy
end

class Flag < ActiveRecord::Base
  belongs_to :flaggable, polymorphic: true
  belongs_to :owner, class_name: "User", foreign_key: :user_id
end

class User < ActiveRecord::Base
  has_many :flags
end

<div>
  <% if user_signed_in? && flaggable.flags.any? { |r| r.user_id == current_user.id } %>
    <p>Flag Submitted</p>
  <% else %>
    <%= link_to "Flag", [:new, flaggable, :flag], remote: true %>
  <% end %>
...
</div>

我认为这会起作用:

<% if user_signed_in? && flaggable.flags.exists?(owner: current_user) %>
  • any? 是一种数组方法,它循环检查是否有任何条件 return 对于当前持有的数组中的每个元素为真 in-memory.

请注意,flaggable.flags 是一个扩展数组 class 的 ActiveRecord::Relation 对象,因此您可以为此使用任何数组方法。但是,一旦你调用任何Array方法,所有flaggable.flags将被存储in-memory,这是到那时,只有到那时才会发生检查匹配条件的循环。

  • exists? 是一种类似于 wherefind 方法的 ActiveRecord 方法,其中检查 return 中的每一行是否有任何条件为真90=] 在数据库中,它不是 in-memory,并且针对搜索进行了更优化,因此效率更高。

更新:(进一步解释)

假设您有此代码

1 @users = User.where(is_admin: true)
2 @users = @users.where(age: 20)
3 <% @users.each do |user| %>
4   <%= user.name %>
5 <% end %>
6 <%= 'THERE IS A SANTA USER' if @users.any?{|user| user.name == 'Santa'} %>
  • 在第 1 行,@users 是一个 ActiveRecord::Relation,其中它还没有访问 DB。它目前只是还在内存中存储数据库查询。

  • 在第 2 行,@users 添加了条件 age should be 20,因此查询现在变成类似于 @users = users-who-are-admin-and-age-is-20。这仍然是一个存储在内存中的查询。仍然没有数据库访问发生。

  • 在第 3 行,从 @users 调用了 .each。通过调用此数组方法,@usersActiveRecord::Relation 现在连接并查询数据库,然后 returns 用户数组现在可用于循环,正如您在 .each 方法。 (您会注意到从 3 开始的行是视图文件中的代码)

  • 在第 6 行,从 @users 调用了 .any?。因为 @users 仍然是 ActiveRecord::Relation 但已经访问了数据库并且数组已经在内存中(因为第 3 行),所以 .any? 将不再访问数据库,并将就像数组 class 的普通 .any? 方法一样。你会发现,如果 DB 中有数百万条 User 记录,那么 @users 将占用大量内存,这不是很好。使用存储 in-memory partial-by-partial 的 .find_each 会更好,但缺点是会调用更多数据库。

附带说明一下,请注意使用 .joins 而不是 .includes.joins return 是一个数组对象,而 .includes return 是一个 ActiveRecord::Relation。两者都有自己的 use-cases.