Rails 命名范围继承?

发布于 2024-11-05 03:05:09 字数 914 浏览 4 评论 0原文

我试图通过提供一个通用的基本模型来概括我的一些模型,该模型包含一些相互的named_scope声明和一个过滤器方法,该方法激活在控制器端搜索更简单的查询。当我在控制台中运行它时,这似乎有效,但在控制器中运行时失败:

# in the base model
class GenericModel < ActiveRecord::Base

  named_scope :by_name, lambda { |name|
    ( name.blank? ) ? {} : { :conditions => [ "#{self.table_name}.name like ?", "%#{name}%" ] }
  }

  def filter(params)
    res = []
    res = self.by_name( (params[:name] or '') ) if params[:name]
    return res
  end

end

class MyModel < GenericModel
  set_table_name 'my_models'
end

# works in in console!
>> params = { :name => 'jimmy' }
>> MyModel.filter(params)
=> [ <#MyModel ...>, ... ]
nil

# fails in controller
@model = MyModel.filter(params)

# ActiveRecord::StatementInvalid (Mysql::Error Unknown column 'generic_models.name' in where clause...)

显然,在 Rails 中时正在调用父类的named_scope,但在 Rails 控制台中运行良好。有什么想法如何解决这个问题吗?谢谢。

I'm trying to generalize some of my models by providing a common base model to inherit from that contains some mutual named_scope declarations and a filter method that activates that search for simpler querying on the controller side. This appears to be working when I run it in the console, but fails when in the controller:

# in the base model
class GenericModel < ActiveRecord::Base

  named_scope :by_name, lambda { |name|
    ( name.blank? ) ? {} : { :conditions => [ "#{self.table_name}.name like ?", "%#{name}%" ] }
  }

  def filter(params)
    res = []
    res = self.by_name( (params[:name] or '') ) if params[:name]
    return res
  end

end

class MyModel < GenericModel
  set_table_name 'my_models'
end

# works in in console!
>> params = { :name => 'jimmy' }
>> MyModel.filter(params)
=> [ <#MyModel ...>, ... ]
nil

# fails in controller
@model = MyModel.filter(params)

# ActiveRecord::StatementInvalid (Mysql::Error Unknown column 'generic_models.name' in where clause...)

Apparently the parent class' named_scope is being called when in rails, but works fine in rails console. Any ideas how to mend this? thanks.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

微暖i 2024-11-12 03:05:09

由于 ActiveRecord 尝试解释您所说内容的方式,这有点像火车失事。通常,从 ActiveRecord::Base 派生的第一个类用于定义基表名称,其子类默认定义为使用单表继承 (STI)。您可以使用 set_table_name 来解决这个问题,但通常情况下,虽然可能违背 Rails 的原则,但事情往往会变得混乱。

按照 Beerlington 的建议,您应该能够使用 mixin 更干净地完成此操作。

module ByNameExtension
  def self.extended(base)
    # This method is called when a class extends with this module

    base.send(:scope, :by_name, lambda { |name|
      name.blank? ? nil : where("#{self.table_name}.name LIKE ?", "%#{name}%")
    })
  end

  def filter(params)
    params[:name].present? ? self.by_name(params[:name]) : [ ]
  end
end

class MyModel < ActiveRecord::Base
  # Load in class-level methods from module ByNameExtension
  extend ByNameExtension
end

您应该能够将扩展包含在该模块中。如果您想进一步清理此问题,请编写一个初始化程序,为 ActiveRecord::Base 定义类似 scoped_by_name 的方法来触发此行为:

class ActiveRecord::Base
  def scoped_by_name
    extend ByNameExtension
  end
end

然后您可以标记所有需要此操作的类:

class MyModel < ActiveRecord::Base
  scoped_by_name
end

That's a bit of a train-wreck because of the way ActiveRecord is trying to interpret what you're saying. Generally the first class derived from ActiveRecord::Base is used to define what the base table name is, and sub-classes of that are defined to use Single Table Inheritance (STI) by default. You're working around this by using set_table_name but, as is often the case, while it's possible to go against the grain in Rails, things often get messy.

You should be able to do this a lot more cleanly using a mixin as suggested by Beerlington.

module ByNameExtension
  def self.extended(base)
    # This method is called when a class extends with this module

    base.send(:scope, :by_name, lambda { |name|
      name.blank? ? nil : where("#{self.table_name}.name LIKE ?", "%#{name}%")
    })
  end

  def filter(params)
    params[:name].present? ? self.by_name(params[:name]) : [ ]
  end
end

class MyModel < ActiveRecord::Base
  # Load in class-level methods from module ByNameExtension
  extend ByNameExtension
end

You should be able to keep your extensions contained to that module. If you want to clean this up even further, write an initializer that defines a method like scoped_by_name for ActiveRecord::Base that triggers this behavior:

class ActiveRecord::Base
  def scoped_by_name
    extend ByNameExtension
  end
end

Then you can tag all classes that require this:

class MyModel < ActiveRecord::Base
  scoped_by_name
end
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文