背景:Rails 7.1,仅使用 DB 交互层,没有 Web 框架,没有服务器等…
我确信这会引起一些不满,但我一直在车辆信息数据库中遇到ActiveRecord::DangerousAttributeError
关于的问题.model_name
。汽车有型号,这些型号有名称。“只需更改数据库”根本不是一个选项。源数据库是外部拥有和管理的。我实际上无法修改它。而且我将该数据库与来自其他公司的外部数据一起使用,所有这些公司都使用、期望和理解“model_name”的概念。我可以在读取数据时物理地修改数据,但还没有找到可以使其工作的解决方案。
所以我想知道我是否可以.model_name
在应用程序加载时从 Rails 中删除该方法。我的应用程序的功能在我的计算机上是本地的,不会在其他地方使用,而且我不会在任何地方使用 Rails 方法。因此,这种方法的存在会给我带来麻烦,而从模型中删除它则不会。
.model_name
的 ActiveModel::Naming 实例方法。对我来说绝对不重要。
这可行吗?
8
最佳答案
2
这是一个极其糟糕的想法。
仅仅因为您没有在代码中直接引用该方法或意识到它的重要性,并不意味着它不是框架的核心部分。
ActiveModel 可能很简单,但是 ActiveModel 使用它来与表单、I18N、多态路由等所有内容进行交互。简而言之,几乎所有使 Rails 约定优于配置方法发挥作用的东西。
在 ActiveRecord 中,该方法用于根据类名查找表、派生外键等。
虽然您可以明确地配置所有内容并避免由此产生的一些错误,但您建议的并不是删除该#model_name
方法 – 它本质上是使用从架构创建的方法对实例方法进行 monkeypatching,这要糟糕得多。
这会打开一个潘多拉盒子,里面有很多错误,因为模型实例仍然会响应#model_name
,并且在隐式地将其用作字符串的地方,您将向 Rails 提供垃圾,而不是真正对应于路由或数据库表的内容,从而导致错误和不可预测的行为。这比直接中断的地方要糟糕得多,因为代码需要一个实例ActiveModel::Name
。
ActiveRecord 不提供任何机制来消除危险方法名称冲突,并且似乎不是您的应用程序的最佳选择。它是一种高度固执己见的 ORM,它假设您完全控制数据库架构并遵循其约定。如果不是,那么您只会让自己的生活变得困难。
使用另一个 ORM(例如 Sequel)或在应用程序和数据库之间添加一个适配器层,例如使用问题较少的列名复制数据库。
|
这是可行的:
# app/models/car.rb
# prevent model_name from raising a DangerousAttributeError
ActiveRecord::AttributeMethods.instance_variable_set(
:@dangerous_attribute_methods,
ActiveRecord::AttributeMethods.dangerous_attribute_methods - ["model_name"]
)
class Car < ApplicationRecord
validate do
errors.add(:model_name, :blank) if self[:model_name].blank?
end
end
Car.new # force generation of attribute methods
Car.const_get(:GeneratedAttributeMethods).remove_method(:model_name)
但需要注意的是,你必须使用以下命令来获取属性值。以下是我所有的测试:
>> Car.first[:model_name]
=> "asdf"
>> Car.first.model_name
=>
#<ActiveModel::Name:0x00007f3cb51e0de0
@collection="cars",
@element="car",
...
>> Car.new.save!
`<main>': Validation failed: Model name can't be blank (ActiveRecord::RecordInvalid)`
>> Car.new(model_name: "a").save!
=> true
>> Car.last
=> #<Car:0x00007f3cb5487300 id: 8, model_name: "a", make: nil, created_at: "2024-10-25 14:35:39.206560000 +0000", updated_at: "2024-10-25 14:35:39.206560000 +0000">
2
-
1似乎您可以通过覆盖以
self.dangerous_attribute_method?
排除model_name
此类,以较小的影响方式执行相同的操作,然后使用alias_attribute :model, :model_name
后跟,def model_name; super; end
尽管我还没有测试过。
–
-
1@engineersmnky 哦,是的,这要简单得多,除非它应该是
def model_name; self.class.model_name; end
–
|
sequel
或者rom
来代替。–
–
–
model_name
你几乎破坏了模型提供的所有功能,例如关联、多态路由、i18n 查找、查询接口、表单绑定等。如果只使用 PORO,通过直接与数据库连接交互来读取/写入数据,那么错误会更少。至少这样你就知道什么是真正有效的。我认为你使用另一种不那么固执己见的 ORM 的想法要好得多。–
–
|