用has_scope实现Rails API筛选

做接口的时候,遇到了筛选的问题,不知如何去处理,Google了下,发现了个很简单的解法,实测有效,附上。

问题描述

现有model product,category, schema中信息如下:

Table name products

id          :integer
title       :string
description :string
price       :float
category_id :integer

Table name  categories
id          :integer
title       :string

app/models/product.rb中:

class Product < ActiveRecord::Base
  belongs_to :category
  ......
end

app/models/category.rb中:

class Category < ActiveRecord::Base
  has_many :products
  ......
end

app/controllers/api/v1/products_controller.rb中:

class Api::V1::ExperiencesController < Api::V1::BaseController
# GET /api/v1/products
  def index
    @products = Product.all
    render json: @products
  end
end

现在需要得到指定category的products,类似这样:/api/v1/products?category=1,得到category是1的所有products。以及给定价格区间的products,类似这样:/api/v1/products?price[from]=100&price[to]=999

如何实现?

试试has_scope

解答

三步走

  • 安装has_scope gem

    gemfile中添加:

    gem 'has_scope'
    

    终端bundle,服务器重启。

  • 在model中添加scope

    修改app/models/product.rb

    class Product < ActiveRecord::Base
      belongs_to :category
    
     + scope :category, -> (category_id) { where(category_id: category_id) }
     + scope :price, -> (from, to) { where("price >= ? AND price <= ?", from, to) }
      ......
    
    end
    
  • 在controller中添加has_scope

    修改app/controllers/api/v1/products_controller.rb

    class Api::V1::ExperiencesController < Api::V1::BaseController
      + has_scope :category, only: :index
      + has_scope :price, using: [:from, :to], only: :index
    
    # GET /api/v1/products
      def index
      -  @products = Product.all
      +  @products = apply_scopes(Product).includes(:category).all
          # using includes(:category), fix N+1 query problem
        render json: @products
      end
    end
    

    好,大功告成,用postman测试下,确认正常,cool!

参考

Rails API Filtering and Sorting

has_scope