我目前正在设置一个实例,其中Review
有很多Flag
s(通过多态),而User
有很多标记。到目前为止一切正常,但我现在想要在视图中如果用户标记了特定评论,则显示消息,标记已提交而不是标记它的链接。
现在我通过首先收集属于评论的所有标志,然后使用any?
确定其中是否有任何一个拥有当前用户的ID来做到这一点。我想知道这样做是否有更快/更有效的方法?它是一个查找链,让我全面地保持原样。
注意: 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>
答案 0 :(得分:1)
我认为这会奏效:
<% if user_signed_in? && flaggable.flags.exists?(owner: current_user) %>
any?
是一个Array方法,循环检查当前保存在内存中的Array中的每个元素是否会返回true。请注意flaggable.flags
是一个扩展Array类的ActiveRecord :: Relation对象,因此您可以使用任何Array方法。但是,一旦调用任何Array方法,所有flaggable.flags
都将存储在内存中,到那时为止,只有在那时才会进行检查匹配条件的循环。
exists?
是一种类似于where
和find
方法的ActiveRecord方法,其中检查数据库中表中的每一行是否有任何条件将返回true -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 Query存储在内存中。
在第2行,@users
添加了age should be 20
条件,因此查询现在变为@users = users-who-are-admin-and-age-is-20
。这仍然是存储在内存中的查询。仍然没有发生数据库访问。
在第3行,.each
调用@users
。通过调用此数组方法,@users
现在ActiveRecord::Relation
连接并查询数据库,数据库然后返回一个现在可用于循环的用户数组,如您在.each
方法中所期望的那样。 (您会注意到从3开始的行是视图文件中的代码)
在第6行,.any?
调用@users
。因为,@users
仍然是ActiveRecord::Relation
但已经访问过数据库并且数组已经在内存中(因为第3行),那么.any?
将不再访问数据库,并且只是表现得像Array类的普通.any?
方法。然后您将意识到,如果数据库中有数百万个用户记录,那么@users
将占用大量内存,这不是很好。最好使用.find_each
存储内存中的部分数据,以及更多数据库调用的缺点。
另外,请注意使用.joins
而不是.includes
。 .joins
返回一个Array对象,而.includes
返回一个ActiveRecord :: Relation。两者都有自己的用例。