【问题标题】:Defining attributes at runtime based on data from related object根据相关对象的数据在运行时定义属性
【发布时间】:2019-03-22 16:20:31
【问题描述】:

我正在构建一个应用程序,其中用户是Organisation 的一部分。一个组织有很多Lists,而这些ListItems 又是很多。

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

到目前为止,在定义未绑定到数据库中特定列的属性时,我使用了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 的上下文中运行的,它是Class 的一个实例,而Class 没有#organisation 方法.我希望用一种有意义的方式措辞1

我尝试过使用after_initialize,但在对象生命周期的那个时刻,#attributeActiveRecord::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 都会运行模型的代码,这意味着每次发生这种情况时都会重新定义虚拟属性,这就是为什么我我在问如何在这两种情况下做到这一点。

【问题讨论】:

  • 请缩小您的问题范围。我需要一些澄清:“初始化或加载列表项时”?你的意思是它们是什么时候创建的?从数据库中选择? “基于位于属于组织的任意数据库列中的数据”任意可能很困难。我想我需要一个例子。
  • @VeridianDynamics 感谢您的评论。我在问题正文中添加了一个示例哈希。我的理解是,如果我的模型主体中有attribute :name, String,那么每次从数据库创建或加载新实例时都会运行它,所以我需要能够在这两种情况下动态地执行此操作。

标签: ruby-on-rails activerecord virtus


【解决方案1】:

在实例化(初始化或加载)记录时,是 有一种方法可以检索存储在一个数据库列中的哈希 它的关系?

是的。只要您的关系设置正确/简单,这相当简单。假设我们有这三个模型:

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 感觉这种东西的用法很奇怪。也许辅助方法会是更好的选择!

【讨论】:

  • 感谢您抽出宝贵时间回答!不幸的是,我的问题似乎仍然太扭曲而没有意义。我在问是否有可能在模型的代码首次运行时获得关系。如果您查看问题中的第一段代码,我会尝试在尚未定义的时刻访问organisation。我的问题是我是否可以在此时以另一种方式访问​​此关系,或者我是否可以在我的类被实例化后使用DocumentSerializable::ClassMethods#attribute 实现,此时该关系将可用。
猜你喜欢
  • 2019-02-13
  • 2012-11-14
  • 1970-01-01
  • 1970-01-01
  • 2012-05-16
  • 1970-01-01
  • 2021-11-03
  • 2015-02-17
  • 2020-09-03
相关资源
最近更新 更多