【问题标题】:Ignore parameters that are null in active record Rails 4忽略活动记录Rails 4中为空的参数
【发布时间】:2016-04-11 15:05:43
【问题描述】:

我创建了一个简单的网络表单,用户可以在其中输入一些搜索条件来查找场地,例如一个价格范围。当用户单击“查找”时,我使用活动记录来查询数据库。如果填写了所有字段,这一切都会很好地工作。当一个或多个字段保持打开状态并因此具有 null 值时会出现问题。

如何在我的控制器中解决这个问题?我应该首先检查一个值是否为空并基于它创建一个查询吗?我可以想象我最终会得到许多不同的查询和大量代码。必须有更快的方法来实现这一点?

控制器:

def search
   @venues = Venue.where("price >= ? AND price <= ? AND romance = ? AND firstdate = ?", params[:minPrice], params[:maxPrice], params[:romance], params[:firstdate])
end

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-4 rails-activerecord


    【解决方案1】:

    您可能希望过滤掉随请求发送的所有空白参数。

    这里是一种快速且 DRY 的解决方案,用于过滤掉空白值,只触发一次数据库查询,并使用 Rails 的 ActiveRecord ORM 构建 where 子句。

    正如@DanBrooking 所指出的,这种方法可以防止 SQL 注入。 Rails 4.0+ 提供了“强大的参数”。您应该使用该功能。

    class VenuesController < ActiveRecord::Base
    
      def search
        # Pass a hash to your query
        @venues = Venue.where(search_params)
      end
    
      private
    
        def search_params
          params.
            # Optionally, whitelist your search parameters with permit
            permit(:min_price, :max_price, :romance, :first_date).
            # Delete any passed params that are nil or empty string
            delete_if {|key, value| value.blank? }
        end
    end
    

    【讨论】:

    • 这是正确答案,应该是公认的答案。接受的答案容易受到 SQL 注入的攻击。
    【解决方案2】:

    我建议在 Venue 中制作方法

    def self.find_by_price(min_price, max_price)
       if min_price && max_price
         where("price between ? and ?", min_price, max_price)
       else
         all
       end
     end
    
    def self.find_by_romance(romance)
       if romance
         where("romance = ?", romance)
       else
         all
       end
     end
    
    def self.find_by_firstdate(firstdate)
       if firstdate
         where("firstdate = ?", firstdate)
       else
         all
       end
     end
    

    并在您的控制器中使用它 Venue .find_by_price(params[:minPrice], params[:maxPrice]) .find_by_romance(params[:romance]) .find_by_firstdate(params[:firstdate])

    【讨论】:

      【解决方案3】:

      这个问题的另一个解决方案,我认为一个更优雅的解决方案是使用带条件的作用域。 你可以做类似的事情

      class Venue < ActiveRecord::Base   
        scope :romance, ->(genre) { where("romance = ?", genre) if genre.present? } 
      end
      

      然后您可以将它们链接起来,如果没有参数存在,它将作为 AND 工作,那么它不是链的一部分。

      http://guides.rubyonrails.org/active_record_querying.html#scopes

      【讨论】:

        【解决方案4】:

        试试下面的代码,它会忽略那些不存在的参数

        conditions = []
        conditions << "price >= '#{params[:minPrice]}'" if params[:minPrice].present?
        conditions << "price <= '#{params[:maxPrice]}'" if params[:maxPrice].present?
        conditions << "romance = '#{params[:romance]}'" if params[:romance].present?
        conditions << "firstdate = '#{params[:firstdate]}'" if params[:firstdate].present?
        
        @venues = Venue.where(conditions.join(" AND "))
        

        【讨论】:

        • 顺便说一句,这种方法不是不容易受到 sql 注入的影响吗? "price >= #{query}" 而不是 "price >= ?, query" ?
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-07-07
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多