根据相关对象的数据在运行时定义属性

时间:2019-03-22 16:20:31

标签: ruby-on-rails activerecord virtus

我正在构建一个应用程序,其中用户是Organisation的一部分。一个组织有许多List,而它们又有许多ListItem

现在,我希望管理员用户能够基于他们所属的组织(或者更确切地说,他们的列表所属的组织)指定列表项上可用的属性,而无需触摸任何代码

到目前为止,在定义未绑定到数据库中特定列的属性时,我使用了document_serializable,这是一个漂亮的小宝石(基于virtus),它将虚拟属性序列化为JSONB。数据库中的列。我喜欢这种方法,因为我可以获得virtus的所有优点(类型,强制,验证等),并且数据最终位于JSONB列中,这意味着可以相对轻松地快速加载,索引和搜索数据

在动态添加这些用户定义的属性时,我想继续使用这种方法。所以我想做类似的事情:

class ListItem < ApplicationRecord
  belongs_to :list
  delegate :organisation, to: :list

  organisation.list_attributes.each do |a, t|
    attribute a, t
  end
end

Organisation#list_attributes返回属性名称及其关联类型的用户定义的哈希值,例如,可能类似于:

{
  name: String,
  age: Integer
}

您可能已经猜到了,这是行不通的,因为organisation.list_attributes.each实际上是在ListItem的上下文中运行的,而ClassClass的实例,而#organisation却没有没有after_initialize方法。我希望以合理的方式措辞 1

我尝试使用#attribute,但是在对象生命周期中,ActiveRecord::AttributeMethods::ReadDocumentSerializable::ClassMethods拥有,而不是Organisation#find拥有,所以这是一种完全不同的方法而且我不知道我是否仍然可以访问所需的那个,甚至可以使用。

另一种选择是以class ListItem < ApplicationRecord belongs_to :list delegate :organisation, to: :list { name: String, age: Integer }.each do |a, t| attribute a, t end end 样式以某种明确的方式找到有问题的组织,但老实说,我不知道该在哪里存储必要的信息。

所以,我的问题是:在实例化(初始化或加载 2 )记录时,有什么方法可以检索存储在其中一个关系的数据库列中的哈希?还是我试图以一种完全误导的方式构建它,如果是这样,我还应该怎么做?


1 澄清一下,如果我像这样直接使用哈希:

{{1}}

这行得通,我的问题只是在这个较早的时间点获得记录的关系。

2 我的理解是,无论何时从数据库创建或加载该类型的记录,Rails都会运行模型的代码,这意味着每次发生时都会重新定义虚拟属性,这就是为什么问这两种情况下该怎么做。

1 个答案:

答案 0 :(得分:0)

  在实例化(初始化或加载)记录时,

是   有一种方法可以检索存储在一个数据库列中的哈希   关系?

是的。只要正确/简单地建立您的关系,这是相当琐碎的。可以说我们有以下三种模型:

class ListItem < ApplicationRecord
  belongs_to :list
end

class List < ApplicationRecord
  belongs_to :organisation
  has_many :list_items
end

class Organisation < ApplicationRecord
  has_many :lists
end

我们可以实例化ListItem,然后从其任何父母那里获取数据。

@list_item = ListItem.find(5) # assume that the proper inherited 
                                foreign_keys exist for this and 
                                its parent
@list = @list_item.list
@hash = @list.organisation.special_hash_of_org

如果我们想在ListItem的每个实例中执行此操作,则可以使用Active Record回调,如下所示:

class ListItem < ApplicationRecord
  belongs_to :list

  # this is called on ListItem.new and whenever we pull from our DB
  after_initialize do |list_item|
    puts "You have initialized a ListItem!"
    list = list_item.list
    hash = list.organisation.special_hash_of_org
  end

end

但是after_initialize对于这种事情感觉很奇怪。也许助手方法会是更好的选择!