【问题标题】:How can I set "global" variables that can be accessed in controllers and models in Rails如何设置可以在 Rails 的控制器和模型中访问的“全局”变量
【发布时间】:2019-07-17 20:00:01
【问题描述】:

我有一个设置条目的表。我想在我的模型和控制器中将这些条目作为变量访问,而无需每次都查询数据库来设置这些变量。

我可以通过为我的模型和控制器创建重复的“关注点”来使其工作。我还可以在我的 ApplicationController 中设置全局变量。或者我可以在我需要它们的每个地方初始化它们。设置和访问可以在控制器和模型中访问的全局变量的正确 rails 方法是什么?

class ItemType
  has_many :items
end

class Item
  belongs_to :item_type
  belongs_to :foo
end

class Foo 
  has_many :items  

  def build_item
    bar_item_type = ItemType.find_by(:name => "bar")

    self.items.build(
      :foo_id => self.id,
      :item_type_id => bar_item_type.id
    )
  end
end

class ItemsController
  def update
    bar_item_type = ItemType.find_by(:name => "bar")

    @item.update(:item_type_id => bar_item_type.id)
  end

end

在示例中,您可以看到我在 Foo 模型和 ItemsController 中都声明了 bar_item_type 变量。我希望能够为我的 rails 项目创建和访问该变量一次,而不必在任何地方进行相同的数据库调用,从而干掉我的代码库。

【问题讨论】:

  • 我不确定您的示例是否清楚说明您为什么要这样做。你真的会在你的应用程序中使用ItemType.find_by(:name => "bar") 吗?在您的用例中使用全局变量会破坏 MVC 和基本的 Rails 约定。你首先需要问自己为什么你认为你需要这样做。可能有更好更传统的方法来做到这一点。
  • 我想这是我的问题。使用上面的示例执行此操作的常规/更好方法是什么?我想要一个在这两个用例中都可以访问的变量。
  • 问题是,你怎么能确定ItemType.find_by(:name => "bar") 甚至会返回任何东西?您的代码对数据库做出假设。
  • 数据库以特定的 item_types 为种子。数据库假定并设计为具有它们。

标签: ruby variables ruby-on-rails-4 global-variables activesupport-concern


【解决方案1】:

我会反对这种硬编码或依赖于数据库状态的代码。如果你必须这样做,这是我知道的一种方法:

# models
class ItemType < ActiveRecord::Base
  has_many :items

  # caches the value after first call
  def self.with_bar
    @@with_bar ||= transaction { find_or_create_by(name: "bar") }
  end

  def self.with_bar_id
    with_bar.id
  end
end

class Item < ActiveRecord::Base
  belongs_to :item_type
  belongs_to :foo

  scope :with_bar_types, -> { where(item_type_id: ItemType.with_bar_id) }
end

class Foo < ActiveRecord::Base
  has_many :items  

  # automatically sets the foo_id, no need to mention explicitly
  # the chained with_bar_types automatically sets the item_type_id to ItemType.with_bar_id
  def build_item
    self.items.with_bar_types.new
  end
end

# Controller
class ItemsController
  def update
    @item.update(item_type_id: ItemType.with_bar_id)
  end
end

【讨论】:

  • OP 可能还想验证项目类型上名称的唯一性
  • @lacostenycoder:那么 OP 应该在帖子的示例代码中提到这一点。无论如何,它很容易绕过它。
  • 我同意,也认为您提供了一个很好的答案。
【解决方案2】:

如果您必须使用常量,有几种方法可以做到。但是您必须考虑到您正在实例化一个 ActiveRecord 模型对象,该对象依赖于数据库中存在的数据。不建议这样做,因为您现在拥有依赖于数据库中存在的数据的模型和控制器逻辑。如果您已经为数据库播种并且它不会改变,这可能没问题。

class ItemType
  BAR_TYPE ||= where(:name => "bar").limit(1).first 

  has_many :items
end

现在,无论您在哪里需要这个对象,都可以这样称呼它:

bar_item_type  = ItemType::BAR_TYPE

【讨论】:

  • 谢谢。我相信这就是我正在寻找的。我不知道您可以像这样在 ItemType 模型上声明 BAR_TYPE
  • 这是有效的 ruby​​ 语法,但不完全是传统的 Rails 风格。 Surya 在下面的回答是更传统的 Rails 方式,并且有一些额外的好处,而我的例子更像是一个 hack。
猜你喜欢
  • 2019-07-16
  • 1970-01-01
  • 2012-07-21
  • 2014-12-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多