如何将值哈希从存储过程转换为 Sequel 模型实例或数据集的数组



>存储过程从一个名为ACCOUNTS的表中返回一个行数组(SETOF(,其关联的Sequel模型Account

[1] pry(#<Psql::CalculateMasterBalancesTest>)> DB.select(Sequel.lit('*')).from{ |o| Sequel.function(:get_master_accounts, DB.row_type(:accounts, @accounts['A/1/1'].values)) }.all
=> [{:id=>1651, :parent_id=>1649, :ban=>nil, :risk=>nil, :custom_attributes=>nil, :created_at=>2017-05-17 19:33:09 +0200, :updated_at=>2017-05-17 19:33:09 +0200},
 {:id=>1649, :parent_id=>1647, :ban=>nil, :risk=>nil, :custom_attributes=>nil, :created_at=>2017-05-17 19:33:09 +0200, :updated_at=>2017-05-17 19:33:09 +0200},
 {:id=>1647, :parent_id=>nil, :ban=>"A Master", :risk=>nil, :custom_attributes=>nil, :created_at=>2017-05-17 19:33:09 +0200, :updated_at=>2017-05-17 19:33:09 +0200}]

为了使它与应用程序的其余部分配合良好,我想将此值哈希数组实例化为Account模型实例数组。

如果我只是在上一个表达式的末尾添加 .map{|hash| Account.new(hash)},我会得到一个

Sequel::MassAssignmentRestriction: id is a restricted primary key

错误。

如果我欺负这个并手动设置 ID 列,我猜很有可能由于 Sequel 中某处的逻辑persisted?某些东西会在某个地方(如果可能的话,在一些奇怪的边缘情况下(咬我(我只是猜测,这就是 ActiveRecord 发生的事情,这似乎是一个常见的设计问题(,

在续集中有没有处理这个问题的惯用方法?

有时写一个问题会给你一个答案:

Account.from{ |o| Sequel.function(:get_ancestry, DB.row_type(:accounts, @accounts['A/1/1'].values)) }.all

工程。


实际上,这真的,真的,非常好,因为它的行为就像任何数据集一样,你可以加入它,"在哪里"它,排序它等,一切都表现得非常好。

  DB.register_row_type(:accounts)
  def ancestry_dataset
    Account.from do |o|
      Sequel.function(:get_ancestry, DB.row_type(:accounts, values))
    end
  end

然后:

def test_me
  acc = @accounts['A/1/1']
  pd = acc.ancestry_dataset
  assert pd.is_a?(Sequel::Postgres::Dataset), 'expected a postgres dataset'
  assert_equal 2, pd.count, 'count returned funny'
  assert_equal 2, pd.all.count, 'all.count returned funny'
  assert_equal 1, pd.where(parent_id: nil).count, 'where(...).count returned funny'
  assert_equal 1, pd.where(parent_id: nil).all.count, 'where(...).all.count returned funny'
  assert_equal [:id], pd.select(:id).first.keys, 'when I restrict the select statement columns, the keys are funny'
end

它工作得很好,没有打嗝,是处理分层数据的绝佳工具。

供将来参考,get_ancestry函数为:

CREATE OR REPLACE FUNCTION get_ancestry(_acct Accounts)
  RETURNS SETOF Accounts AS $$
DECLARE
  cur_acct Accounts%ROWTYPE;
BEGIN
  -- we start from the parent of _acct
  SELECT INTO cur_acct *
  FROM Accounts
  WHERE id = _acct.parent_id;
  WHILE (cur_acct.id IS NOT NULL) LOOP
    RAISE NOTICE 'get_ancestry: returning %', cur_acct.id;
    RETURN NEXT cur_acct;
    SELECT INTO cur_acct *
    FROM Accounts
    WHERE id = cur_acct.parent_id;
  END LOOP;
END;
$$ LANGUAGE plpgsql;
">

id 是受限制的主键"是因为您传入了记录的:id

尝试实例化新帐户之前,请从值中删除:id

.select(Sequel.lit('*'))

应该没有必要。续集自动选择*

require 'json'
require 'sequel'
DB = Sequel.sqlite
DB.create_table :items do
  primary_key :id
  String :name
  Float :price
end
items = DB[:items]
items.insert(:name => 'abc', :price => rand * 100)
class Item < Sequel::Model(:items)
end
Item.dataset.sql
# => "SELECT * FROM `items`"

最新更新