0

我正在构建一个应用程序,其中用户是Organisation. 一个组织有很多Lists,而后者又拥有很多ListItems。

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

到目前为止,在定义未绑定到数据库中特定列的属性时,我使用了document_serializable,这是一个漂亮的小 gem(基于virtus),它将虚拟属性序列化为 db 中的 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

WhereOrganisation#list_attributes返回属性名称及其关联类型的用户定义哈希,例如,可能如下所示:

{
  name: String,
  age: Integer
}

正如您可能已经猜到的那样,这不起作用,因为organisation.list_attributes.each实际上是在 的上下文中运行的ListItem,它是 的实例Class,并且Class没有#organisation方法。我希望以一种有意义的方式措辞1

我试过使用after_initialize,但在对象生命周期的那个时刻,#attribute是由ActiveRecord::AttributeMethods::Read而不是拥有的DocumentSerializable::ClassMethods,所以这是一种完全不同的方法,我不知道我是否仍然可以访问我需要的那个,以及它是否甚至可以工作.

另一种选择是以某种明确的方式找到有问题的组织,Organisation#find-style,但老实说,我不知道我应该在哪里存储必要的信息。

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


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

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

  {
    name: String,
    age: Integer
  }.each do |a, t|
    attribute a, t
  end
end

它会起作用,我的问题只是在这个较早的时间点获得记录的关系。

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

4

1 回答 1

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

我们可以实例化 aListItem然后从它的任何父母那里检索数据。

@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

如果我们想在 a 的每个实例上都这样做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感觉这种东西的用法很奇怪。也许辅助方法会是更好的选择!

于 2019-03-22T18:15:25.083 回答