Ruby on rails activeadmin和动态存储访问器在新资源上失败

Ruby on rails activeadmin和动态存储访问器在新资源上失败,ruby-on-rails,dynamic,activeadmin,jsonschema,jsonb,Ruby On Rails,Dynamic,Activeadmin,Jsonschema,Jsonb,我希望为具有postgresjsonb列:data的资源生成表单,并希望将这些表单的模式存储在数据库的表中。经过大量的研究,我有90%在那里,但我的方法在创建(而不是更新)时在ActiveAdmin表单中失败。有人能解释一下吗 抱歉,代码片段太长了。这是一个相当复杂的设置,但我认为它会引起一些兴趣,因为如果它起作用,可以动态地构建任意的新模式,而无需硬编码 我跟随Rails 6、ActiveAdmin 2.6.1和ruby 2.6.5 我想将Json模式存储在属于SampleAction的表Sa

我希望为具有postgresjsonb列:data的资源生成表单,并希望将这些表单的模式存储在数据库的表中。经过大量的研究,我有90%在那里,但我的方法在创建(而不是更新)时在ActiveAdmin表单中失败。有人能解释一下吗

抱歉,代码片段太长了。这是一个相当复杂的设置,但我认为它会引起一些兴趣,因为如果它起作用,可以动态地构建任意的新模式,而无需硬编码

我跟随Rails 6、ActiveAdmin 2.6.1和ruby 2.6.5

我想将Json模式存储在属于SampleAction的表SampleActionSchema中(使用Json模式gem进行验证)

为了使该系统在AA表单中工作,我使用参数{category:“cleve”}调用正常路径(新建或编辑)_admix_sample_action_表单,然后动态生成permit_参数:

ActiveAdmin.register SampleAction, namespace: :admix do

  permit_params do
    prms=[:name, :category, :data, :sample_id, :created_at, :updated_at]
    #the first case is creating a new record (gets parameter from admix/sample_actions/new?category="xxx"
    #the second case is updating an existing record
    #falls back to blank (no extra parameters)
    categ = @_params[:category] || (@_params[:sample_action][:category] if @_params[:sample_action]) || nil
    cat=SampleActionSchema.where(category: categ)
    if cat.size>0 && !cat[0].schema.empty?
      cat[0].schema["properties"].each do |key, value|
        prms+=[key.to_sym]
      end
    end
    prms
  end

form do |f|
    f.semantic_errors
    new=f.object.new_record?
    cat=params[:category] || f.object.category
    f.object.category=cat if cat && new
    f.object.add_field_accessors if new
    sas=SampleActionSchema.where(category: cat)
    is_schema=(sas.size>0) && !sas[0].schema.empty?
    if session[:active_sample]
      f.object.sample_id=session[:active_sample]
    end

    f.inputs "Sample Action" do
      f.input :sample_id
      f.input :name
      f.input :category
      if !is_schema
        f.input :data, as: :jsonb
      else
        f.object.schema_properties.each do |key, value|
        f.input key.to_sym, as: :string
        end
      end
    end
    f.actions
  end
如果我正在编辑一个现有资源(如上面控制台中创建的),那么一切都可以正常工作。提交时,将显示表单并更新所有动态字段。但是当创建一个新的资源时,例如:data是data的形式:{quality:“good”}我得到

我尝试在表单中添加_访问器,并重写新命令以在初始化后添加访问器(不需要这些访问器,因为ActiveRecord回调似乎在正确的时间执行任务)

在AA控制器中创建资源时,似乎不可能存储访问器,即使它在控制台中工作正常。是否有人有正确初始化资源的策略?

解决方案:

我追踪了AA是如何计算出所需命令的最小数量的。有必要添加代码来构建新的资源,以确保AA构建的任何新资源都具有正确的:category字段,并且一旦这样做,就可以调用来动态地将存储访问器键添加到新构建的实例中

现在,用户可以创建自己的原始模式和使用它们的记录,而无需进一步编程!我希望其他人觉得这很有用,我一定会的

这里有两个丑陋的解决方案,一个是AA不希望将参数添加到活动管理新路由调用中,但它仍然有效。我想这个参数可以通过其他方式传递,但是快速脏的方法可以完成这项工作。另一个是,我必须让表单生成一个会话变量来存储使用了什么样的模式,以便post表单提交构建知道,因为按下“CREATEMOVE”按钮将清除url中的参数

操作如下:对于名为Move with field的模型:应根据json模式表将数据动态序列化为字段的数据
admin/moves/new?category=“cleave”
admin/moves/#/edit
从架构表中找到“cleave”架构,并使用序列化参数正确创建和填充表单。并且,直接写入数据库

m=Move.new(category: "cleave")          ==> true
m.update(name: "t2", quality: "fine")   ==> true
按预期工作。架构表定义为:

require "json-schema"
class SampleActionSchema < ApplicationRecord
  validates :category, uniqueness: { case_sensitive: false }, allow_nil: false, allow_blank: true
  validate :schema_is_json_schema

  def self.schema_keys(categ)
    sas=SampleActionSchema.find_by(category: categ)
    schema_keys= sas.nil? ? [] : sas[:schema]["properties"].keys.map{|k| k.to_sym}
  end

  private

  def schema_is_json_schema
    metaschema = JSON::Validator.validator_for_name("draft4").metaschema
    unless JSON::Validator.validate(metaschema, schema)
       errors.add :schema, 'not a compliant json schema'
    end
  end
end
解决方案:

我追踪了AA是如何计算出所需命令的最小数量的。有必要添加代码来构建新的资源,以确保AA构建的任何新资源都具有正确的:category字段,并且一旦这样做,就可以调用来动态地将存储访问器键添加到新构建的实例中

现在,用户可以创建自己的原始模式和使用它们的记录,而无需进一步编程!我希望其他人觉得这很有用,我一定会的

这里有两个丑陋的解决方案,一个是AA不希望将参数添加到活动管理新路由调用中,但它仍然有效。我想这个参数可以通过其他方式传递,但是快速脏的方法可以完成这项工作。另一个是,我必须让表单生成一个会话变量来存储使用了什么样的模式,以便post表单提交构建知道,因为按下“CREATEMOVE”按钮将清除url中的参数

操作如下:对于名为Move with field的模型:应根据json模式表将数据动态序列化为字段的数据
admin/moves/new?category=“cleave”
admin/moves/#/edit
从架构表中找到“cleave”架构,并使用序列化参数正确创建和填充表单。并且,直接写入数据库

m=Move.new(category: "cleave")          ==> true
m.update(name: "t2", quality: "fine")   ==> true
按预期工作。架构表定义为:

require "json-schema"
class SampleActionSchema < ApplicationRecord
  validates :category, uniqueness: { case_sensitive: false }, allow_nil: false, allow_blank: true
  validate :schema_is_json_schema

  def self.schema_keys(categ)
    sas=SampleActionSchema.find_by(category: categ)
    schema_keys= sas.nil? ? [] : sas[:schema]["properties"].keys.map{|k| k.to_sym}
  end

  private

  def schema_is_json_schema
    metaschema = JSON::Validator.validator_for_name("draft4").metaschema
    unless JSON::Validator.validate(metaschema, schema)
       errors.add :schema, 'not a compliant json schema'
    end
  end
end

我发现一个问题是,在用户提交记录时,activeadmin调用build\u new\u resource,从而触发add\u store\u accessor的执行。由于此新资源为空,:category未定义,因此存储访问器将被清除。因此,参数定义“质量”丢失。我发现一个问题是,当用户提交记录时,activeadmin调用build\u new\u resource,从而触发add\u store\u accessor的执行。由于此新资源为空,:category未定义,因此存储访问器将被清除。因此,参数定义“质量”丢失。还在想办法做什么。
def new
  build_resource
  resource.add_field_accessors
  new!
end
m=Move.new(category: "cleave")          ==> true
m.update(name: "t2", quality: "fine")   ==> true
require "json-schema"
class SampleActionSchema < ApplicationRecord
  validates :category, uniqueness: { case_sensitive: false }, allow_nil: false, allow_blank: true
  validate :schema_is_json_schema

  def self.schema_keys(categ)
    sas=SampleActionSchema.find_by(category: categ)
    schema_keys= sas.nil? ? [] : sas[:schema]["properties"].keys.map{|k| k.to_sym}
  end

  private

  def schema_is_json_schema
    metaschema = JSON::Validator.validator_for_name("draft4").metaschema
    unless JSON::Validator.validate(metaschema, schema)
       errors.add :schema, 'not a compliant json schema'
    end
  end
end
class Move < ApplicationRecord
  after_initialize :add_field_accessors

  def add_field_accessors
    if category!=""
      keys=SampleActionSchema.schema_keys(category)
      keys.each {|k| singleton_class.class_eval{store_accessor :data, k}}
    end
  end
end
ActiveAdmin.register Move do
  permit_params do
    #choice 1 is for new records, choice 2 is for editing existing
    categ = @_params[:category] || (@_params[:move][:category] if @_params[:move]) || ""
    keys=SampleActionSchema.schema_keys(categ)
    prms = [:name, :data] + keys
  end

  form do |f|
    new=f.object.new_record?
    f.object.category=params[:category] if new
    if new
      session[:current_category]=params[:category]
      f.object.add_field_accessors
    else
      session[:current_category] = ""
    end
    keys=SampleActionSchema.schema_keys(f.object.category)
    f.inputs do
      f.input :name
      f.input :category
      keys.each {|k| f.input k}
    end
    f.actions
  end

  controller do
   def build_new_resource
    r=super
    r.assign_attributes(category: session[:current_category])
    r.add_field_accessors
    r
   end
  end
end