背景:阅读Practical Object Oriented Design in Ruby后,我试图重构代码(这很棒),为此,我想引入更多封装责任的模型,而不是使用一个逻辑大文件(和案例说明)。
问题:为了简化问题陈述,我有一个模型“ Rule
”,其中“有很多” RuleConditions
。但是,数据库中只有一个表用于规则。在其中,我有一栏针对类型为jsonb
的条件(基于RuleCondition
的复杂性)。但我似乎无法实现这一目标。具体来说,我无法弄清楚如何使用嵌套模型实例化模型,并且希望ActiveRecord知道如何将模型转换为jsonb,甚至可能从表转换回嵌套模型。我也不知道是否可以在没有表使用ActiveRecord支持的情况下定义has_many
关系。
我期望的是
我希望应该有一些流程(由ActiveRecord和ActiveModel混合定义),使该流程成为可能
Rule
的参数。RuleConditions
数组中创建一个
规则参数的子集。Rule.new(rule)
RuleCondition
rule.save!
Rule
属性的嵌套RuleConditions
模型重建conditions
。我尝试过的事情:
我原本以为我会进入serialize, :conditions, JSON
的途中,但是要序列化我的对象很困难。在那之后,我真的不知道。我也玩过ActiveModel :: Conversion。所以我只需要一些指导。
而且,非常清楚,在我的as_json
上调用RuleCondition
就像我期望的那样(打印出以前存储在Rule
模型中的JSON,数据库,然后尝试进行重构)。因此,可能是我不了解serialize
(因为除非另有说明,否则应该使用YAML,因此我认为编码不同于“匹配我的列类型”)
编辑:
当前我有类似(准系统,0个验证/关联)
class Rule < ActiveRecord::Base
end
class RuleController < ApplicationController
def create
rule = Rule.new(rule_params[:rule]) # conditions are just an attribute in the params
rule.save
end
end
现在,使用定义为的新模型
class RuleCondition
include ActiveModel::Model # (what I'm currently doing to get some of the behavior of a model without the persistence / table backing it, I think)
attr_accessor :noun, :subnoun # etc
end
我认为我需要这样做
def create
rule = rule_params[:rule]
rule["conditions"] = rule["conditions"].map do |c|
RuleCondition.new(c)
end
true_rule = Rule.new(rule)
true_rule.save!
end
但这是行不通的,因为(完全)这个原因:
18:13:52 web.1 | SQL(10.7ms)插入“规则”(“名称”,“条件”,“ created_at”,“ updated_at”)值($ 1,$ 2,$ 3,$ 4)返回“ id” [[[“ name”,“ wefw” ],[“ conditions”,“ {#}”],[“ created_at”,“ 2018-12-16 02:13:52.938849”],[“ updated_at”,“ 2018-12-16 02:13:52.938849” ]] 18:13:52 web.1 | PG :: InvalidTextRepresentation:错误:json类型的输入语法无效 18:13:52 web.1 | 详细信息:令牌“#”无效。 18:13:52 web.1 |上下文:JSON数据,第1行:#... 18:13:52 web.1 | :插入“规则”(“名称”,“条件”,“ created_at”,“ updated_at”)值($ 1,$ 2,$ 3,$ 4)返回“ id” 18:13:52 web.1 | (0.5ms)回滚
答案 0 :(得分:2)
请记住,数据库适配器会处理某些序列化任务 为了你。例如:PostgreSQL中的json和jsonb类型将是 在JSON对象/数组语法和Ruby Hash或Array之间转换 透明对象。在这种情况下,无需使用序列化。
-api.rubyonrails.org
请勿将serialize
与本机JSON / JSONB列一起使用。它的意思是与字符串列一起用作穷人替代。
您试图做的事情实际上超出了ActiveRecord的范围-AR是围绕关系模型构建的,其中模型与表相对应。而且,您不能指望AR会有任何规定将JSONB列解组为基本标量类型以外的任何内容。我会考虑,相对于为关系创建一个单独的表,您所做的工作是否真的值得您付出努力?
使用ActiveModel :: Model可以使您的模型走上正确的轨道,它将为您的模型提供与常规模型相同的行为,但是您应该看看ActiveRecord handles nested attributes的情况:
class Rule < ApplicationRecord
def conditions_attributes=(attributes)
attributes.each do |a|
# you would need to implement this method
unless RuleCondition.reject_attributes?(a)
self.conditions << RuleCondition.new(c)
end
end
end
end
您可以通过创建设置器/获取器来模仿关联的其他方面。
但是再一次,您可以只创建一个带有JSONB列和一对多或m2m关联的rule_conditions表,而实际上将您的时间花在生产上。