Ruby on rails Rails STI:如何更改类名和;'的值;类型';柱
由于公司规定,我不能使用我们的域名类名;我将用一个类比来代替。我有一个名为projects的表,其中有一个名为“type”的列,可能的值为“introductor”和“outdoor”。具有室内和室外功能的记录具有明确的功能分离,并且作为STI实现非常适合。不幸的是,我无法更改类型名称,也无法在全局命名空间中添加类。有没有办法为“type”指定不同的值Ruby on rails Rails STI:如何更改类名和;'的值;类型';柱,ruby-on-rails,types,single-table-inheritance,sti,Ruby On Rails,Types,Single Table Inheritance,Sti,由于公司规定,我不能使用我们的域名类名;我将用一个类比来代替。我有一个名为projects的表,其中有一个名为“type”的列,可能的值为“introductor”和“outdoor”。具有室内和室外功能的记录具有明确的功能分离,并且作为STI实现非常适合。不幸的是,我无法更改类型名称,也无法在全局命名空间中添加类。有没有办法为“type”指定不同的值 注意:我没有尝试为STI使用不同的列名而不是“type”。除了我的类名之外,我希望类型有一个不同的值。这是可能的,但有点复杂。基本上,当您保存继
注意:我没有尝试为STI使用不同的列名而不是“type”。除了我的类名之外,我希望类型有一个不同的值。这是可能的,但有点复杂。基本上,当您保存继承类的记录时,该方法会向上移动到具有添加的“type”值的父类 标准定义:
class Vehicle < ActiveRecord::Base
..
def type=(sType)
end
..
end
class Truck < Vehicle
# calling save here will trigger a save on a vehicle object with type=Truck
end
我自己没有使用这个,但很简单,在保存之前,您应该能够非常轻松地更改类型
列值。这可能会导致内置的活动记录查询接口和关联出现一些问题
此外,您还可以执行以下操作:
class Truck
..
def self.model_name
"MyNewClassName"
end
..
end
但使用这种方法时,请注意,rails的其余部分,包括路由和控制器,将把模型称为“MyNewClassName”,而不是“Truck”
PS:如果不能在全局名称空间中添加类,那么为什么不在另一个名称空间中添加它们呢?父级和子级可以属于不同的名称空间,也可以同时属于唯一的名称空间(而不是全局名称空间)。您可以尝试以下方法:
class Proyect < ActiveRecord::Base
end
class Proyect
然后是室内班,但用别的名字
class Other < Proyect
class << self
def find_sti_class(type_name)
type_name = self.name
super
end
def sti_name
"Indoor"
end
end
end
class-Other class如果只需要从类型
字段中剥离模块名称,则可以使用以下基于VAIRIX答案的帮助器模块:
# lib/util/namespaceless_sti.rb
module NamespacelessSti
def find_sti_class(type_name)
type_name = self.name
super
end
def sti_name
self.name.sub(/^.*:/,"")
end
end
该模块必须扩展到每个STI基类,例如OP描述中的项目
(想象模型生活在Acme
命名空间/模块中)
#app/models/acme/project.rb
需要“util/namespaceless_sti”
模块顶点
类项目
这确保了Acme::indior
项目中的projects
记录将在其type
字段中有“indior”。查看源代码时,似乎有一个store\u full\u sti\u class
选项(我不知道它是何时引入的):
config.active\u record.store\u full\u sti\u class=false
在Rails 5.1.4中,@VAIRIX的解决方案对我不起作用,因为我有整数类型的继承列,Rails find_sti_类在第一行有额外的强制转换:
type_name = base_class.type_for_attribute(inheritance_column).cast(type_name)
我面临着演员阵容的问题。即使我准备了正确的类名。强制转换需要整数并将任何字符串更改为0。
这就是为什么我扩展了#compute_type,并将store_full_sti_class设置为false:
class Parent < ApplicationRecord
self.table_name = 'PolyTable'
self.inheritance_column = 'RecordType'
self.store_full_sti_class = false
class << self
def compute_type(type_name)
type_name = case type_name.to_s
when "4"
"ChildOfType4"
...
else
type_name
end
super
end
end
end
类父级 类在Rails 5中,Attributes API允许我们更改任何列的序列化,包括STI模型的type
列,因此:
# lib/my_sti_type.rb
class MyStiType < ActiveRecord::Type::String
def cast_value(value)
case value
when 'indoor'; 'App::CarpetProject'
when 'outdoor'; 'App::LawnProject'
else super
end
end
def serialize(value)
case value
when 'App::CarpetProject'; 'indoor'
when 'App::LawnProject'; 'outdoor'
else super
end
end
def changed_in_place?(original_value_for_database, value)
original_value_for_database != serialize(value)
end
end
# config/initializers/types.rb
require 'my_sti_type'
ActiveRecord::Type.register(:my_sti_type, MyStiType)
# app/models/project.rb
class Project < ActiveRecord::Base
attribute :type, :my_sti_type
end
#lib/my_sti_type.rb
类MyStiType
根据需要替换您自己的类名和字符串匹配/操作。有关示例,请参见
同样的机制也适用于多态关联的attributename\u type
列。到目前为止你试过什么吗?没有,我什么都没试过。我在谷歌上找不到一个好的结果,这个结果会给我一个方向的暗示。@VAIRIX提供的解决方案非常有效!我的代码在名称空间中。默认情况下,在数据库中设置的类型为“namespace::Classname”。当我说我不能将我的类添加到全局名称空间时,我的意思是,仅仅因为我“必须”将类型值保留为“室内”,我不想将我的类移动到顶级名称空间。find_sti_class
覆盖不应该移到基类吗?这是我通常放的地方。请注意,您使用这种方法,通过基类找到的对象将被实例化为基类,而不是子类。例如,Proyect.new(type:'Other')将返回Proyect类型的对象,其属性:type设置为'Other'。而默认行为是返回一个已被强制转换为类型Other的对象。@LikeMaBell您是对的,您基本上不能通过实例化基类来创建类型为“室内”
的记录。我已经覆盖了基类和子类中的两个方法find_sti_class
和sti_name
,但我无法创建带有以下内容的记录:Proyect.new(类型:“室内”)
因为我得到:ActiveRecord::SubclassNotFound:无效的单表继承类型:室内不是Proyect的子类
同时Proyect.new(类型:'Other')。保存代码>返回ActiveRecord::RecordInvalid:验证失败:T
class Parent < ApplicationRecord
self.table_name = 'PolyTable'
self.inheritance_column = 'RecordType'
self.store_full_sti_class = false
class << self
def compute_type(type_name)
type_name = case type_name.to_s
when "4"
"ChildOfType4"
...
else
type_name
end
super
end
end
end
# lib/my_sti_type.rb
class MyStiType < ActiveRecord::Type::String
def cast_value(value)
case value
when 'indoor'; 'App::CarpetProject'
when 'outdoor'; 'App::LawnProject'
else super
end
end
def serialize(value)
case value
when 'App::CarpetProject'; 'indoor'
when 'App::LawnProject'; 'outdoor'
else super
end
end
def changed_in_place?(original_value_for_database, value)
original_value_for_database != serialize(value)
end
end
# config/initializers/types.rb
require 'my_sti_type'
ActiveRecord::Type.register(:my_sti_type, MyStiType)
# app/models/project.rb
class Project < ActiveRecord::Base
attribute :type, :my_sti_type
end