【问题标题】:Rails: saving filter conditions on attributes and checking whether conditions pass?Rails:保存属性的过滤条件并检查条件是否通过?
【发布时间】:2014-11-20 21:42:09
【问题描述】:

我有一个 SaaS 应用程序,其中一个帐户有很多用户。

在帐户中,帐户所有者可以对申请加入帐户的用户指定某些过滤器。例如,他可以设置必须满足这些要求条件才能接受用户:user.age > 21, user.name.count > 4。

我正在考虑创建一个属于 Account 的 FilterCondition 模型,其中一行可能看起来像 account_id: 1, attribute: "age", condition_string: "> 21"account_id: 1, attribute: "phone_type", condition_string: "== iphone"

然后当我只想接受满足这些条件的用户时,请执行以下操作

  #User.rb
  def passes_requirements?
    account.filter_conditions.each do |filter_conditions|
      attr = filter_condition.attribute
      return false if self.attr != filter_condition.condition
    end 
    true
  end

我知道这是完全错误的语法,但它应该能说明我想要做什么。

任何建议的方法让帐户保存要求条件,然后检查用户以查看他们是否满足这些要求?

【问题讨论】:

    标签: sql ruby-on-rails ruby ruby-on-rails-4 filter


    【解决方案1】:

    如果将条件字符串分成比较器(例如>)和要比较的值会更容易:

    class FilterCondition < ActiveRecord::Base
      belongs_to :account
    
      validates :attribute, presence: true
      validates :comparator, presence: true
      validates :value, presence: true
    
      def matching_users(query_chain = User.where(account: account))
        query_chain.where("#{attribute} #{safe_comparator} ?", value)
      end
    
      private
      def safe_comparator
        safe_values = ['=', '>', '>=', '<', '<='] # etc
        return comparator if safe_values.include? comparator
        ''
      end
    end
    

    safe_comparator 方法降低了 SQL 注入查询的风险。链接过滤器集合有点棘手,但类似以下想法的方法应该可行。

    class Account < ActiveRecord::Base
      #....
      has_many :filter_conditions
      def filtered_users
        query = User.where(account: self)
        filter_conditions.each do |filter_condition|
          query = filter_condition.matching_users(query)
        end
        query
      end
    
    end
    
    
    account = Account.first
    
    filter_1 = FilterCondition.create(
      account: account,
      attribute: :age,
      comparator: '>=',
      value: 21
    )
    filter_2 = FilterCondition.create(
      account: account,
      attribute: :age,
      comparator: '<=',
      value: 99
    )
    
    
    account.filtered_users
    

    【讨论】:

    • 如果我们也想比较字符串,这会起作用吗?所以像“用户手机类型必须是iPhone或Android”,它会过滤掉其他手机的用户
    • 只要FilterConditionvalue 的DB 类型是字符串就应该这样做。生成的 ActiveRecord 查询将是 where("phone_type = ?", 'iPhone')。执行 OR 查询(例如 Android 或 iPhone)更难,需要重组 FilterCondition(但并非不可能)
    • query_chain.where("#{attribute} #{safe_comparator} ?", value) => 我不认为 #{safe_comparator} 实际上变成了比较器。它仍然是字符串类型。我们有什么办法可以做 #{safe_comparator.to_comparator} 或类似的事情吗?
    • 它应该是一个字符串。本质上,您发送的是 Arel/ActiveRecord 查询字符串以及一堆值。然后问号以 sql-safe 方式替换为值,整个字符串变成 SQL 查询。
    • 这是我们最终提出的解决方案:gist.github.com/kibaekr/64908308ba189680cc49
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-10-14
    • 1970-01-01
    • 2011-07-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多