Я хочу сгенерировать формы для ресурса, имеющего postgres jsonb column: data, и хочу, чтобы схема этих форм хранилась в таблице в базе данных. После долгих исследований я нахожусь на 90%, но мой метод не работает в формах ActiveAdmin при создании (не обновлении). Кто-нибудь может это объяснить?
Извините за длинные фрагменты кода. Это довольно сложная настройка, но я думаю, что она представляет некоторый интерес, поскольку, если это сработает, можно будет создавать произвольные новые схемы динамически без жесткого кодирования.
Я следую этому предыдущему обсуждению с Rails 6 и ActiveAdmin 2.6.1 и ruby 2.6.5.
Я хочу сохранить Json схемы в таблице SampleActionSchema, которые принадлежат к SampleAction (используя гем json -schema для проверки)
class SampleActionSchema < ApplicationRecord
validates :category, uniqueness: { case_sensitive: false }, allow_nil: false, allow_blank: true
validate :schema_is_json_schema
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 SampleAction < ActiveRecord::Base
belongs_to :sample
validate :is_sample_action
validates :name, uniqueness: { case_sensitive: false }
after_initialize :add_field_accessors
before_create :add_field_accessors
before_update :add_field_accessors
def add_store_accessor field_name
singleton_class.class_eval {store_accessor :data, field_name.to_sym}
end
def add_field_accessors
num_fields = schema_properties.try(:keys).try(:count) || 0
schema_properties.keys.each {|field_name| add_store_accessor field_name} if num_fields > 0
end
def schema_properties
schema_arr=SampleActionSchema.where(category: category)
if schema_arr.size>0
sc=schema_arr[0]
if !sc.schema.empty?
props=sc.schema["properties"]
else
props=[]
end
else
[]
end
end
private
def is_sample_action
sa=SampleActionSchema.where(category: category)
errors.add :category, 'not a known sample action' unless (sa.size>0)
errors.add :base, 'incorrect json format' unless (sa.size>0) && JSON::Validator.validate(sa[0].schema, data)
end
end
Все работает корректно; Например, для простой схемы с именем category: "cleave", где: data выглядит как data: {quality: "good"}
, я могу создать ресурс в консоли rails следующим образом:
sa=SampleAction.new(sample_id: 6, name: "test0", data: {}, category: "cleave" )
=> #<SampleAction id: nil, name: "test0", category: "cleave", data: {}, created_at: nil, updated_at: nil, sample_id: 6>
sa.quality = "good" => true
sa.save => true
Чтобы эта система работала в Формы AA, я вызываю обычный путь (новый или редактируемый) _admix_sample_action_form с params: {category: "cleave"}, а затем динамически генерирую allow_params:
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
Все работает нормально, если я редактирую существующий ресурс (как создано в консоли выше). Форма отображается, и все поля Dynami c обновляются при отправке. Но при создании нового ресурса, где, например: данные имеют форму data: {quality: "good"}, я получаю
ActiveModel::UnknownAttributeError in Admix::SampleActionsController#create
unknown attribute 'quality' for SampleAction.
Я попытался как add_accessors в форме, так и переопределить новую команду, чтобы добавить аксессоры после инициализации (они не нужны, потому что обратный вызов ActiveRecord, кажется, выполняет работу в нужное время).
def new
build_resource
resource.add_field_accessors
new!
end
Каким-то образом, когда ресурс создается в контроллере AA, кажется невозможным сохранить аксессоры, даже если они нормально работают в консоли. У кого-нибудь есть стратегия по правильной инициализации ресурса?