【问题标题】:Rails has_many conditionsRails has_many 条件
【发布时间】:2010-03-30 17:24:08
【问题描述】:
    c = "(f.profile_id = #{self.id} OR f.friend_id = #{self.id})"
    c += AND + "(CASE WHEN f.profile_id=#{self.id} THEN f.friend_id ELSE f.profile_id END = p.id)"
    c += AND + "(CASE WHEN f.profile_id=#{self.id} THEN f.profile_rejected ELSE f.friend_rejected END = 1)"
    c += AND + "(p.banned = 0)"

我需要在这样的 has_many 关系中使用它:

    has_many :removed_friends, :conditions => ???

我如何设置 self.id?,或者我如何传递 id? 然后我想使用 will_paginate 插件:

    @profile.removed_friends.paginate(:page => 1, :per_page => 20)

感谢您的帮助

编辑:

 class Profile < ActiveRecord::Base
    has_many :friendships
    has_many :removed_friends, :class_name => 'Profile', :through => :friendships, :conditions => 
        "(friendships.profile_id = #{self.id} OR friendships.friend_id = #{self.id})"
        "AND (CASE WHEN friendships.profile_id=#{self.id} THEN friendships.profile_rejected ELSE friendships.friend_rejected END = 1)" + 
        "AND (p.banned = 0)"
  end


class Friendship < ActiveRecord::Base
  belongs_to :profile
  belongs_to :removed_friend, :class_name => 'Profile', :foreign_key => "(CASE WHEN friendships.profile_id = #{self.id} THEN friend_id ELSE profile_id END)"
end

【问题讨论】:

    标签: ruby-on-rails will-paginate


    【解决方案1】:

    使用单引号将条件括起来:

    class Profile < ActiveRecord::Base
      has_many :friendships
      has_many :removed_friends, :class_name => 'Profile', :through => :friendships, 
                                 :conditions => '
        ( friendships.profile_id = #{self.id} OR 
          friendships.friend_id = #{self.id}
        ) AND
        (CASE WHEN friendships.profile_id=#{self.id} 
              THEN friendships.profile_rejected 
              ELSE friendships.friend_rejected 
         END = 1
        ) AND 
        (p.banned = 0)'
    end
    

    【讨论】:

    • 你必须使用单引号才能工作。我一直用这个。
    • 这会让您面临 SQL 注入攻击吗?在没有参数绑定的情况下看到 #{self.id} 令人担忧。
    • 注意,这个技巧在 Rails 3.05 及更高版本中已被弃用。您可以使用 proc 或 finder_sql 完成同样的事情
    • @JohnNaegle 在这种情况下不太可能进行 SQL 注入,因为代码正在替换模型属性而不是用户输入。偏离路线总是有可能黑客以某种方式设法将虚假 SQL 存储在 id 字段中(不太可能)。我只对id 字段使用这种方法。
    • 仅将其用于 ID 字段是有意义的,我想将其用于其他属性。
    【解决方案2】:

    您可能希望将其分解为一系列命名范围,这些范围可以分阶段应用,而不是一次全部应用。例如,提取被禁止的部分:

    class Friend < ActiveRecord::Base
      named_scope :banned, lambda { |*banned| {
        :conditions => { :banned => banned.empty? ? 1 : (banned.first ? 1 : 0) }
      }}
    end
    
    @profile.friends.removed.banned(false).paginate(:page => 1, :per_page => 20)
    

    在关系中使用繁重的条件必然会造成麻烦。如果可能,请尝试对表进行非规范化,创建具有“简单”数据版本的派生列,或进行其他操作以简化查询。

    【讨论】:

    • 不是问题所在。我会告诉你我想做什么。我将编辑我以前的问题..
    【解决方案3】:

    你在这里真的有两个关系。你有:

    • profile_id 一方拒绝的友谊
    • friend_id 一方被拒绝的友谊

    我不知道为什么双方都可以拒绝友谊,也许你需要在这里稍微看看你的模型(哪一方在请求它?最好考虑请求者取消请求而不是说它被profile 方面拒绝了?)

    无论如何,我会将其建模为两个独立的关系:

    class Profile
      has_many :rejected_friendships, :conditions => 'friendships.profile_rejected = 1'
      has_many :canceled_friendships, :foreign_key => 'friend_id', :conditions => 'friendships.friend_rejected = 1'
    
      named_scope :banned, lambda do |*banned| 
          { :conditions => {:banned => banned.empty? ? 1 : (banned.first ? 1 : 0) } }
      end
    
      has_many :rejected_friends, :class_name => 'Profile', :through => :rejected_friendships
      has_many :canceled_friends, :class_name => 'Profile', :through => :canceled_friendships
    
      def removed_friends
        (self.rejected_friends.banned(false).all + self.canceled_friends.banned(false).all).uniq
      end
    end
    

    这有点不受欢迎,因为 removed_friends 不再是关系,所以你不能再做像Profile.removed_friends.find(:all, :conditions =&gt; {:name =&gt; "bleh"}) 这样的事情,但这是一个相当复杂的情况。这个条件相当复杂。

    【讨论】:

    • 在我的数据库中是这样的:profile_id,是发出请求的那个,friend_id,是接收好友请求的那个,那么有一个profile_rejected,它最初是假的,但是如果profile_id拒绝友谊将被设置为真。然后是一个错误的friend_rejected。 then 是一个接受的 col,它是错误的,并且在friend_id 接受之后,友谊将是真实的。 union ... + ... 真的很糟糕,因为我无法通过排序、限制和偏移提出要求才能正常工作。无论如何,谢谢你的时间,很快我会更好地解释你我的意思。我很快就回来了……
    猜你喜欢
    • 2017-10-09
    • 2014-04-24
    • 1970-01-01
    • 1970-01-01
    • 2017-04-21
    • 1970-01-01
    • 2012-08-22
    • 2012-11-11
    • 1970-01-01
    相关资源
    最近更新 更多