太空检查的检查太多了。如何在Rails5中优化对ActiveRecord的查询?

时间:2016-12-08 02:26:49

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

在进行查询之前,我正在检查空参数。 params [:car_model_id] 只有1次检查。我可以想象如果我要为其他参数添加更多检查,那么会有一堆if-else语句。它看起来并不好看,我认为它可以进行优化。但是怎么样?这是控制器的代码:

class CarsController < ApplicationController
def search
  if params[:car_model_id].empty?
    @cars = Car.where(
      used: ActiveRecord::Type::Boolean.new.cast(params[:used]), 
      year: params[:year_from]..params[:year_to], 
      price: params[:price_from]..params[:price_to], 
      condition: params[:condition]
      )
  else
    @cars = Car.where(
      used: ActiveRecord::Type::Boolean.new.cast(params[:used]), 
      car_model_id: params[:car_model_id], 
      year: params[:year_from]..params[:year_to], 
      price: params[:price_from]..params[:price_to], 
      condition: params[:condition]
      )
  end

  if @cars
    render json: @cars
  else
    render json: @cars.errors, status: :unprocessable_entity
  end
end
end

1 个答案:

答案 0 :(得分:1)

诀窍是删除空白值,对数据进行一些预处理(并可能验证),然后将参数传递给where子句。

为了帮助处理日期范围,您可以创建一个方法来检查提供的日期并将其转换为范围:

def convert_to_range(start_date, end_date)
  if start_date && end_date
    price_from = Date.parse(price_from)
    price_to = Date.parse(price_to)
    price_from..price_to
  end
rescue ArgumentError => e
  # If you're code reaches here then the user has invalid date and you       
  # need to work out how to handle this.
end

然后您的控制器操作可能如下所示:

# select only the params that are need
car_params = params.slice(:car_model_id, :used, :year_from, :year_to, :price_from, :price_to, :condition)

# do some processing of the data
year_from = car_params.delete(:year_from).presence
year_to = car_params.delete(:year_to).presence
car_params[:price] = convert_to_range(year_from, year_to)

price_from = car_params.delete(:price_from).presence
price_to = car_params.delete(:price_to).presence
car_params[:price] = convert_to_range(price_from, price_to)

# select only params that are present
car_params = car_params.select {|k, v| v.present? }

# search for the cars
@cars = Car.where(car_params)

此外,我非常确定used值会在提供给where时自动转换为布尔值。

此外,@carsActiveRecord::Relation,没有errors方法。也许你的意思是根据是否有车返回来给出不同的结果?

例如:@cars.any?(或@cars.load.any?如果您不想执行两个查询来获取汽车并检查汽车是否存在)

修改

正如mu is too short所述,您还可以通过链接where条件和scopes来清理代码。范围有助于将功能移出控制器并进入模型,从而提高功能的可重用性。

E.g。

class Car > ActiveRecord::Base

  scope :year_between, ->(from, to) { where(year: from..to) }
  scope :price_between, ->(from, to) { where(price: from..to) }

  scope :used, ->(value = true) { where(used: used) }

end

然后在你的控制器中:

# initial condition is all cars
cars = Cars.all

# refine results with params provided by user
cars = cars.where(car_model_id: params[:car_model_id]) if params[:car_model_id].present?
cars = cars.year_between(params[:year_from], params[:year_to])
cars = cars.price_between(params[:price_from], params[:price_to])
cars = cars.used(params[:used])
cars = cars.where(condition: params[:condition]) if params[:condition].present?

@cars = cars