每个相关模型实例的活动管理作用域

11
我在使用动态 active admin 范围时遇到了问题。我尝试为我的应用程序中每个“项目”的“经理”创建一个范围。然而,当新经理被创建(或分配给项目)时,这些范围似乎没有更新,但如果重新启动服务器,则会更新。所以代码“有效”,但显然不是我想要的方式。作为ruby / rails新手,我不确定是否需要以某种方式“刷新”范围。
值得一提的是,我在Heroku Cedar上使用Rails 3.2和ActiveAdmin。
以下是相关代码(代码有效,但仅在重新启动服务器后才会带入新的经理):
Manager.find_each do |m|
  scope m.first_name do |projects|
    projects.where(:manager_id => m.id)
  end
end

整个 Active Admin 项目模型:

ActiveAdmin.register Project do
 menu :priority => 1
 index do
  column :name
  column :company_name
  column :status
  column :projection do |project|
   number_to_currency project.projection
  end
  column :updated_at
  default_actions
 end

 scope :all
 scope :working, :default => true do |projects|
  projects.where(:status => 'working')
 end

 Manager.find_each do |m|
  scope m.first_name do |projects|
    projects.where(:manager_id => m.id)
  end
 end
end

下一个答案非常好。我不明白为什么你没有将其标记为已回答。 - Pedro Rolo
6个回答

16
这是解决该问题的实际方案...虽然使用过滤器在稳定性和维护方面更可取,但在ActiveAdmin中这看起来更漂亮,并且更加用户友好,因为作用域变成了漂亮的选项卡。
这有点像hack,但在适当的情况下是可行的解决方案:
技巧是在控制器索引操作的before_filter中更新作用域。
如果您在资源上创建了许多作用域,则可能会出现问题(尽管您可以轻松设置一些限制)。
ActiveAdmin.register Project do
  menu :priority => 1
  index do
    column :name
    column :company_name
    column :status
    column :projection do |project|
      number_to_currency project.projection
    end
    column :updated_at
    default_actions
  end

  scope :all
  scope :working, :default => true do |projects|
    projects.where(:status => 'working')
  end

  controller do
    before_filter :update_scopes, :only => :index

    def update_scopes
      resource = active_admin_config

      Manager.all.each do |m|
        next if resource.scopes.any? { |scope| scope.name == m.first_name }
        resource.scopes << (ActiveAdmin::Scope.new m.first_name do |projects|
          projects.where(:manager_id => m.id)
        end)
      end

      # try something like this for deletions (untested)
      resource.scopes.delete_if do |scope|
        !(Manager.all.any? { |m| scope.name == m.first_name } || ['all', 'working'].include?(scope.name)) # don't delete other scopes you have defined
      end

    end
  end
end

感谢Github上的Daxter提出了一些建议: 相关GitHub问题 - Brant Sterling Wedel
当它们被删除时怎么办? - Pedro Rolo
1
我没有设置来测试它,但我刚刚添加了一些代码,应该有助于删除。 - Brant Sterling Wedel
这个很好运作,但是default范围需要事先设置。 - Weston Ganger

4
我发现这对我有用:
ActiveAdmin 文件
scope :working, :default => true do |projects|
  Project.working
end

模型

scope :working, -> { where(:status => 'working') }

虽然回复有些晚,但希望能对某些人有所帮助。


2
这真的可以创建多个作用域吗?我只看到创建了一个作用域。 - James

2

在AA注册块内真正的动态范围不起作用。我的意思是,管理器表中的更改不会反映在在“初始化”时创建的动态范围中。另请参见: https://github.com/gregbell/active_admin/wiki/Creating-dynamic-scopes。你可以尝试使用过滤器代替范围。然后你可以写出这样的东西:

filter :managers, :as => :select, :collection => proc { Manager.order('name ASC').map(&:first_name) }  

“经过页面刷新后,管理者属性的更改将显示出来,而无需重新启动服务器。还要检查https://github.com/gregbell/active_admin/issues/1261#issuecomment-5296549。此外,请注意,活动记录范围与活动管理员范围是不同的!您可能需要检查。”

http://apidock.com/rails/ActiveRecord/NamedScope/ClassMethods/scope


1
谢谢!将集合封装在proc中是正确的,这样它就可以按需运行。这也解决了rake db:schema:loadrake db:migrate问题。如果不封装在proc中,ActiveAdmin过滤器将尝试在创建模式之前访问模式。 - scarver2

1
这是基于Brant Sterling Wedel答案的优化解决方案。
此解决方案包含以下额外增强功能:
  • 删除任何未使用的范围
  • 允许设置默认范围
### Default scope must be set in advance
scope "Jeff", default: true do |x| 
  x.joins(:manager).where(manager: {name: "Jeff"})
end

controller do

  before_action :reload_scopes, only: :index

  def reload_scopes
    resource_config = active_admin_config

    resource_config.instance_variable_set(:@scopes, [
      resource_config.scopes.first ### Default scope
    ])

    Manager.all.each do |m|
      next if resource_config.scopes.first.name ==  m.name

      resource_config.scopes << ActiveAdmin::Scope.new(m.name){|scope| scope.where(manager_id: m.id) }
    end
  end

end

1
在生产模式下,Rails 只加载一次类。这意味着您的作用域只被调用一次,然后它们被缓存。这就是为什么新的作用域直到重新启动后才会出现的原因。如果您编辑了经理的名字,那么情况也是如此。
我认为解决方案可能是使用 lambda 或 Proc,但在我尝试了几分钟后,我没有成功。这也许是 activeadmin 写法上的限制所致。

这是正确的,这是不可能的。Activeadmin实现了它们的作用域包装器,围绕着活动记录作用域方法,禁止了这种操作。也许可以在保存/提交钩子后以某种方式向activerecord公开“reload_scope”方法。你有任何想法吗? - Sjors Branderhorst

0

Weston Ganger提出的解决方案非常有效。如果你没有默认作用域,可以将all作用域作为默认作用域。

  scope :all, default: true

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接