【问题标题】:Rails & ActiveRecord - refactoring multiple identical where clausesRails & ActiveRecord - 重构多个相同的 where 子句
【发布时间】:2017-12-13 09:13:22
【问题描述】:

我在 Rails 5 上。

我必须处理一个包含多个相同 where 子句的非常复杂的查询:

::AllocatedBudget.joins(:account_code, :budget, account_code: [:place],
                          budget: [:fiscal_year, :budget_state])
                    .where(immeuble:            { id: place.id })
                    .where(situation_budget:    { codesituation: ['A', 'V']})
                    .where(plan_comptable:      { supprime: 'false' })
                    .where(budget:              { supprime: 'false'})
                    .where(situation_budget:    { supprime: 'false' })
                    .where(budget_previsionnel: { supprime: 'false' })
                    .where(exercice_comptable:  { supprime: 'false' })

首先,您必须知道我的模型连接到一个名称难看的旧数据库。我注意到 ActiveRecord 需要自定义名称而不是模型名称来执行查询。我不知道为什么,但它只能这样工作......如果有人能解释它会很好;)

我真正的问题是:我可以用更好的方式来写它吗?有很多次相同的where子句“supprime = 'false'”。

非常感谢! :)

【问题讨论】:

  • 好吧,您可以将所有where 方法条件合并为一个。您也可以将{ supprime: 'false' } 替换为在调用之前定义的具有相同值的变量。
  • @potashin where 子句更好吗?会更难阅读,有什么好处吗?
  • 可以在模型中定义范围并在此处使用它们。
  • potashin 你是对的,我可以这样做并将 {supprime: false } 放在一个 var 中并稍微澄清语法:) thx ! @JagdeepSingh 是的,“连接”和所有“位置”可能是一个很大的范围。我试试

标签: mysql ruby-on-rails activerecord


【解决方案1】:

我会将问题分为两个步骤

  1. 使用纯 ruby​​ 代码创建所有条件的哈希
  2. 将整个哈希传递到where

所以第一步是这样的。

same_conditions_list = [
  :plan_comptable, 
  :budget, 
  :situation_budget, 
  :budget_previsionnel, 
  :exercice_comptable
]

same_conditions_key_values = same_conditions_list.inject({}) do |conditions, condition|
  conditions[condition] = { supprime: 'false' }
  conditions
end   

same_conditions = Hash[same_conditions_key_values]      

all_conditions = same_conditions.merge({
  immeuble: { id: "place.id" }
})       

之后all_conditions 将等于这个

{
  :plan_comptable=>{:supprime=>"false"},
  :budget=>{:supprime=>"false"},
  :situation_budget=>{:supprime=>"false"},
  :budget_previsionnel=>{:supprime=>"false"},
  :exercice_comptable=>{:supprime=>"false"},
  :immeuble=>{:id=>"place.id"}
}

那么第二步就是

::AllocatedBudget.joins(:account_code, :budget, account_code: [:place],
                      budget: [:fiscal_year, :budget_state])
                  .where(all_conditions)

【讨论】:

  • 哇听起来棒极了!我会玩它,我会回来给你我的反馈!谢谢安德鲁!
【解决方案2】:

最后我尝试了设计模式“查询对象”与 AndrewSwerlick 解决方案的混合

module QueryObject
  class AllocatedBudgetQuery

    def self.call(filters = {}, relation = ::AllocatedBudget.all)
      new(filters, relation).tap(&:call)
    end

    # filter can have the key/value as follow (key are symbols) :
    # place_id/string
    # current_fiscal_year/boolean
    # budget_state/['A', 'V', '*']
    def initialize(filters = {}, relation = ::AllocatedBudget.all)
      @relation     = relation
      @filters      = filters
    end

    def call
      conditions = {
        budget_previsionnel: { supprime: 'false' },
        budget: { supprime: 'false' }
      }

      # place filter
      conditions = conditions.merge(immeuble: { id: @filters[:place_id] }) if @filters.key?(:place_id)

      # situation budget filter
      conditions = conditions.merge(situation_budget: { codesituation: @filters[:budget_state] }) if @filters.key?(:budget_state)

      # main extract
      @relation = @relation.joins(:account_code,
                                  account_code: [:place],
                                  budget: %i[fiscal_year budget_state])
                           .where(conditions)

      # current fiscal year filter
      @relation = @relation.where("#{Date.today.to_s} between exercice_comptable.datedebutexercice and exercice_comptable.datefinexercice") if @filters.key?(:current_fiscal_year)
    end
  end
end

【讨论】:

    猜你喜欢
    • 2011-08-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-04-11
    • 1970-01-01
    • 1970-01-01
    • 2021-07-18
    • 1970-01-01
    相关资源
    最近更新 更多