Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/ruby-on-rails/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Ruby on rails Rails STI:如何更改类名和;'的值;类型';柱_Ruby On Rails_Types_Single Table Inheritance_Sti - Fatal编程技术网

Ruby on rails Rails STI:如何更改类名和;'的值;类型';柱

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”。除了我的类名之外,我希望类型有一个不同的值。这是可能的,但有点复杂。基本上,当您保存继

由于公司规定,我不能使用我们的域名类名;我将用一个类比来代替。我有一个名为projects的表,其中有一个名为“type”的列,可能的值为“introductor”和“outdoor”。具有室内和室外功能的记录具有明确的功能分离,并且作为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-Otherclass如果只需要从
类型
字段中剥离模块名称,则可以使用以下基于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