目前,现有的作用域是这样的。
module TransactionScopes
extend ActiveSupport::Concern
included do
scope :status, ->(status) { where status: status }
scope :portfolio_id, ->(portfolio_id) { where portfolio_id: portfolio_id }
scope :investor_external_reference_id, ->(investor_external_reference_id) { where investor_external_reference_id: investor_external_reference_id }
scope :portfolio_external_reference_id, ->(portfolio_external_reference_id) { where portfolio_external_reference_id: portfolio_external_reference_id }
scope :file_id, ->(file_id) { where back_office_file_id: file_id }
scope :oms_status, ->(status) { where oms_status: status }
scope :order_id, ->(order_id) { where order_id: order_id }
scope :order_status, ->(order_status) { where order_status: order_status }
scope :transaction_id, ->(transaction_id) { where transaction_id: transaction_id }
end
我有更多类似作用域的模型,我可以用更通用的方式编写,这样我就可以避免这些重复的过程。
1条答案
按热度按时间sirbozc51#
我强烈建议您不要为每个属性添加范围。
where(...)
只有5个字符,为读者提供了额外的上下文。Person.where(name: 'John Doe')
说:在Person
执行查询(where
)并返回符合条件的集合name: 'John Doe'
.如果添加“建议”属性范围,则该行变为
Person.name('John Doe')
. 通过删除这是一个查询的上下文,读者必须“了解”每个属性名也可以作为作用域访问。上面立即显示了另一个问题,即名称冲突。
Person.name
已获取,并返回类名。所以加上scope :name, ->(name) { where(name: name) }
将引发一个错误。作用域可能很有用,但如果使用太多,则会使模型的类方法命名空间变得混乱。
有了以上这些,下面是一些实际的解决方案。
您可以编写一个帮助器,使您能够轻松地为属性创建作用域。然后循环遍历传递的属性,并为它们动态创建作用域。
然后在模型中调用此帮助器。
或者,如果要为每个属性创建范围,可以使用
attribute_names
. 然后循环遍历它们,并根据名称创建作用域。在上面的片段中
.reject { |attr| respond_to?(attr, true) }
是可选的,但防止创建与当前公共/私有类方法名称冲突的作用域。这将跳过这些属性。您可以放心地省略这一行,但是scope
方法在传递危险的作用域名称时可能引发argumenterror。现在唯一要做的就是打电话
enable_attribute_scopes
在要启用属性作用域的模型中。上面的内容应该让你了解如何处理事情,你甚至可以添加如下选项
:except
或:only
. 还可以选择将上述代码提取到模块中,然后extend AttributeScopeHelpers
在内部ApplicationRecord
如果类变得杂乱无章。然而,就像我开始回答这个问题一样,我建议不要为每个属性添加作用域。