【问题标题】:Is there a DRY approach to applying a filter hash in a RAILS API controller index action?是否有在 RAILS API 控制器索引操作中应用过滤器哈希的 DRY 方法?
【发布时间】:2015-10-20 03:49:22
【问题描述】:

根据 JSON API 规范,我们应该使用过滤器查询参数来过滤控制器中的记录。过滤器参数实际上是什么并没有真正指定,但是由于它应该能够包含多个搜索条件,所以显而易见的事情就是使用哈希。

问题是,对于不同类型的记录,我似乎经常在控制器操作中重复自己。

下面是仅包含 id 列表(以获取多个特定记录)的过滤器的外观。

def index
  if params[:filter] and params[:filter][:id]
    ids = params[:filter][:id].split(",").map(&:to_i)
    videos = Video.find(ids)
  else
    videos = Video.all
  end
  render json: videos
end

对于嵌套属性检查,我想我可以使用 fetchandand,但它仍然看起来不够干,而且我仍然在不同的控制器上做同样的事情。

有没有办法让我看起来更好,而不是重复自己那么多?

【问题讨论】:

    标签: ruby-on-rails dry rails-api json-api


    【解决方案1】:

    这是我对此的看法,部分改编自Justin Weiss' method

    # app/models/concerns/filterable.rb
    module Filterable
      extend ActiveSupport::Concern
    
      class_methods do
        def filter(params)
          return self.all unless params.key? :filter
    
          params[:filter].inject(self) do |query, (attribute, value)|
            query.where(attribute.to_sym => value) if value.present?
          end
        end
      end
    end
    
    # app/models/user.rb
    class User < ActiveRecord::Base
      include Filterable
    end
    
    # app/controllers/users_controller.rb
    class UsersController < ApplicationController
      # GET /users
      # GET /users?filter[attribute]=value
      def index
        @users = User.filter(filter_params)
      end
    
      private
        # Define which attributes can this model be filtered by
        def filter_params
          params.permit(filter: :username)
        end
    end
    

    然后您将通过发出GET /users?filter[username]=joe 来过滤结果。这也适用于没有过滤器(返回 User.all)或没有值的过滤器(它们被简单地跳过)。

    filter 用于遵守 JSON-API。通过关注模型,您可以保持代码干燥,并且只将其包含在您想要过滤的任何模型中。我还使用了强大的参数来针对“可怕的互联网”实施某种保护。

    当然你可以自定义这个关注点,让它支持数组作为过滤器的值。

    【讨论】:

      【解决方案2】:

      与其使用关注点在多个位置包含相同的代码,这似乎是服务对象的一个​​很好的用途。

      class CollectionFilter
          def initialize(filters={})
              @filters = filters
          end
      
          def results
              model_class.find(ids)
          end
      
          def ids
              return [] unless @filters[:id]
              @filters[:id].split(",").map(&:to_i)
          end
      
          def model_class
              raise NotImplementedError
          end
      end
      

      您可以像上面那样编写一个通用的CollectionFilter,然后为特定用例添加子类。

      class VideoFilter < CollectionFilter
          def results
              super.where(name: name)
          end
      
          def name
              @filters[:name]
          end
      
          def model_class
              Video
          end
      end
      

      您可以在控制器中使用它,如下所示;

      def index
          videos = VideoFilter.new(params[:filter]).results
          render json: videos
      end
      

      【讨论】:

      • 比关注方法好得多
      【解决方案3】:

      你可以用Rails Concerns来晒干...

          ##================add common in app/models/concerns/common.rb
          module Common
            extend ActiveSupport::Concern  
      
            #  included do
            ##add common scopes /validations
            # end
      
            ##NOTE:add any instance method outside this module
            module ClassMethods
              def Find_using_filters (params)
                Rails.logger.info "calling class method in concern=======#{params}=="
                ##Do whatever you want with params now
                #you can even use switch case in case there are multiple models
      
              end
          end
        end
      
      ##======================include the concern in model
      include Common
      
      ##=======================in your controller,call it directly    
       Image.Find_using_filters params
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2014-11-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-11-12
        • 1970-01-01
        • 2016-07-06
        相关资源
        最近更新 更多