Rails-在管理员控制器中找不到子模型,为什么



如果有一个模型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还很陌生,如果有什么不清楚的话,那就是同位语。

对此有两种方法。

第一种是在与父类相同的文件中定义子类:

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可能尚未定义,也可能尚未定义。它在另一个可能还没有加载的文件中,除非你已经明确地定义了require。底线是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作为所有食物类的父类就更简单了。

由于某种原因,我不得不在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型号不需要它。

最新更新