【问题标题】:Rails - Child model is not found in an administrate controller, why?Rails - 在管理控制器中找不到子模型,为什么?
【发布时间】:2022-01-04 10:50:52
【问题描述】:

如果有一个模型 Donut 是抽象模型 Food 的子模型: (这也适用于Food 的所有其他孩子)

# models/food.rb
class Food < ApplicationRecord
  self.abstract_class = true
  ...
end
# models/food/donut.rb
class Food::Donut < Food
  ...
end

在我所有使用 Food::Donut 的应用程序中都可以正常工作,除了在管理控制器中:

# controllers/manage/application_controller.rb
module Manage
  class ApplicationController < Administrate::ApplicationController
    ...
  end
end
# controllers/manage/tree_controller.rb
module Manage
  class TreesController < Manage::ApplicationController
    def some_method
      Tree.cut # This works as intended
      Food::Donut.eat # This fails with NameError: uninitialized constant Manage::Food::Donut
    end
  end
end

在控制台中,我通过定义 FoodDonut 设法“让它工作”:

FoodDonut = Food::Donut
module Manage
    class FakeController
        def fake_method
            puts FoodDonut.eat # Works
            puts Food::Donut.eat # fails
        end
    end
end

但在我的代码中这样做似乎会破坏所有其他控制器。

而且我不明白为什么对 Tree 有效,但对 Food::Donut 无效?

我对 ruby​​ 还很陌生,如果有任何不清楚的地方请多多包涵。

【问题讨论】:

  • 你有Manage::Food的课吗?如果存在,Ruby 从Manage::FakeController 开始查找并询问“是否有Manage::FakeController::Food?不。有Manage::Food 吗?是的。好的,肯定有Manage::Food::Donut 然后......不!好吧,那么这是一个错误”。
  • 不,除了管理类中的控制器,我什么都没有。

标签: ruby-on-rails ruby


【解决方案1】:

您有两种解决方法。

首先是在与父类相同的文件中定义子类:

class Food < ApplicationRecord
  # ...
  class Donut < Food
    # ...
  end
end

第二个(我推荐)是避免将 Food 作为父类,而是使用其他东西,例如 Food::Base

# food.rb
module Food
  # ...
end

# food/base.rb
module Food
  class Base < ApplicationRecord
  end
end

# food/donut.rb
module Food
  class Donut < Food::Base
    # ...
  end
end

现在,至于为什么会发生这种情况,它有点 Ruby 和 Rails 的自动加载器。这绝对不是管理员的错 :-) 所以我建议您删除 administrate 标记。问题在于这个定义:

# models/food/donut.rb
class Food::Donut < Food
  ...
end

当 Ruby 读取此文件时,Food 可能尚未定义,也可能尚未定义。它位于另一个可能尚未加载的文件中,除非您明确地required 它。底线是 Ruby 无法在命名空间 Food 中定义 Donut,因为它不知道 Food 是什么。

Rails 可以尝试自动加载它,但由于某种原因它没有。我不太了解自动加载的详细信息,无法告诉您原因。我现在无法使用 Rails 6.1 重现此问题,这可能取决于您的 Rails 版本。

此外,Ruby 不能假设 Food 是一个类并在现场创建它,原因有两个:1)它可能是一个模块而不是一个类,2)它可以从某些东西继承而我们不这样做不知道这是什么东西。 (实际上,在您的示例中,它确实继承自 ApplicationRecord)。

在定义嵌套类和模块时,请始终遵循以下指南:

#
# bad
#

# the_namespace.rb
module TheNamespace # could be a class
  # ...
end

# the_namespace/the_class.rb
class TheNamespace::TheClass
  # ...
end

#
# good
#

# the_namespace.rb
module TheNamespace # could be a class
  # ...
end

# the_namespace/the_class.rb
module TheNamespace # could be a class
  class TheClass
    # ...
  end
end

但是你的情况有点复杂,因为命名空间类也继承自其他东西(来自ApplicationRecord)。因此,正如我在开始时建议的那样,只使用一个不同的类 Base 作为所有食物类的父类会更简单。

【讨论】:

    【解决方案2】:

    出于某种原因,我不得不在Food::Donut 前面添加::

    # controllers/manage/tree_controller.rb
    module Manage
      class TreesController < Manage::ApplicationController
        def some_method
          Tree.cut # This works as intended
          ::Food::Donut.eat
        end
      end
    end
    

    这是让我到达那里的答案: https://stackoverflow.com/a/2842864/4047185

    我仍然不明白为什么 Tree 模型不需要它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2019-11-01
      • 2013-01-03
      • 1970-01-01
      • 1970-01-01
      • 2011-07-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多