【问题标题】:Ruby On Rails - DB Structure for saving CRUD operation parameter/payloadRuby On Rails - 用于保存 CRUD 操作参数/有效负载的数据库结构
【发布时间】:2021-07-26 20:02:19
【问题描述】:

我想要什么

我想制作一个表格,将“CRUD 操作信息本身”保存在我的数据库中。

例如)
create new row {"name": "Tom", "price": 200}
find a row WHERE {"age": 30}
delete row WHERE {"name": "John"}
update the row WHERE {"name": "Ted"} SET {"price": 300}

我的想法

为了实现上述目的,我创建了两个模型。

①CrudOperation  
t.string :crud_type # this should be one of CRUD ("create", "read", "update", "delete")  
t.string :target_database   



②CrudOperationParameter  
t.integer :crud_operation_id # reference  
t.string :key  
t.value :value

CrudOperation 有很多 CrudOperationParameter-s。

问题

我的模型似乎运行良好,除了 crud_type 是“更新”。

这样

①新建行{"name": "Tom", "price": 200}

**CrudOperation**  
id: 1  
crud_type: "create"  
target_database: "XXXX"  

**CrudOperationParameter**  
crud_operation_id: 1  
key: "name"  
value "Tom"  

**Another CrudOperationParameter**  
crud_operation_id: 1  
key: "price"  
value "200"  

但是在注册 CrudOperation 的更新类型时,问题就出现了。

④更新行 WHERE {"name": "Ted"} SET {"price": 300}

**CrudOperation**  
id: 1  
crud_type: "update"  
target_database: "XXXX"  

**CrudOperationParameter**  
crud_operation_id: 1  
key: "name"  
value "Ted"  

**Another CrudOperationParameter**  
crud_operation_id: 1  
key: "price"  
value "300"  

由于 CrudOperationParameter 只有键值列, 我无法确定这个 CrudOperationParameter 是用于 UPDATE 语句中的 WHERE 子句还是 SET 子句。

你能教我更好的数据库架构来保存这些类型的数据吗?

【问题讨论】:

    标签: ruby-on-rails database schema


    【解决方案1】:

    您所拥有的基本上是 Entity Attribute Value pattern 的半生不熟版本(或反模式,具体取决于您询问的对象)。

    如果真的需要,我会将其设置为:

    class CrudOperation < ApplicationRecord
      has_many :crud_operation_parameters
      accepts_nested_attributes_for :crud_operation_parameters
      enum crud_type: {
        create: "create",
        read: "read", 
        update: "update",
        delete: "delete"
      }
    
      # Convenience setter that maps a hash of attributes 
      # into a an array of key value attibutes suitible for `accepts_nested_attributes_for`
      # and sets the nested attributes
      # @return [Array]
      def parameters=(hash)
        self.crud_operation_parameters_attributes = hash.map do |key, value|
           {
             key: key,
             value: value
           }
        end
      end
    end
    
    class CrudOperationParameter < ApplicationRecord
      belongs_to :crud_operation
    end
    
    # 1. create new row {"name": "Tom", "price": 200}
    CrudOperation.create!(
      crud_type: :create,
      parameters: { 
        name: "Tom", 
        price: 200
      }
    )
    
    # 1. create new row {"name": "Tom", "price": 200}
    CrudOperation.create!(
      crud_type: :update,
      parameters: { 
        name: "Tom", 
        price: 200
      }
    )
    
    
    

    我提到这是一个半生不熟的尝试,因为您缺少属性表,您可以在其中规范化属性的定义并存储类型信息等内容。相反,您的解决方案有一个字符串列,其中包含大量可能变得非规范化的重复项。

    但现代 RDBMS 系统具有 JSON、JSONB 和 HSTORE 等列类型,可用于代替 EAV 来存储不适合给定模式的数据。

    与 EAV 不同,您不必将所有属性存储在单个列类型(通常是字符串)中并进行类型转换或创建一组属性表来存储不同类型的属性(例如 StringCrudOperationParameter 和 FloatCrudOperationParameter)。

    在 Postgres 上使用 JSONB,我会将其设置为:

    # rails g model crud_operation crud_type:string payload:jsonb conditions:jsonb
    class CrudOperation < ApplicationRecord
      enum crud_type: {
        create: "create",
        read: "read", 
        update: "update",
        delete: "delete"
      }
    end
    
    # 1. create new row {"name": "Tom", "price": 200}
    CrudOperation.create!(
      crud_type: :create,
      payload: {
        name: "Tom", 
        price: 200
      }
    )
    
    # 2. find a row WHERE {"age": 30}
    CrudOperation.create!(
      crud_type: :read,
      conditions: {
        age: 30
      }
    )
    
    # 3. delete row WHERE {"name": "John"}
    CrudOperation.create!(
      crud_type: :delete,
      conditions: {
        name: "John"
      }
    )
    
    # 4. update the row WHERE {"name": "Ted"} SET {"price": 300}
    CrudOperation.create!(
      crud_type: :update,
      conditions: {
        name: "John"
      },
      payload: {
        price: 300
      }
    )
    

    【讨论】:

    • 感谢您的支持!您的 CrudOperationParameter 有两个关联:crud_operation 和 crud_operation_attribute。请问“crud_operation_attribute”的作用是什么?
    • 对不起,我实际上是想删除它。我最初计划添加一个属性表,但简化了示例。如果您有一个属性规范化表,那么您将使用它而不是在key 表中存储一堆字符串。如果您查看链接inviqa.com/blog/…,他们有一些很好的 EAV 表设置图表来解释它。
    • 基本上通过存储外键,您可以避免一些非规范化问题,例如具有colourcolor 的行,并且可以使用更有效的索引,但代价是额外的连接。
    • 非常感谢您的精彩评论!我读了你发表的文章。就我而言,我永远不会调用像 CrudOperation.crud_operation_attributes(name: "attribute_name") 这样的查询,因为 CrudOperation 将用于在我的应用程序之外调用 CRUD API。这个案子怎么样?
    • 这里的目的是什么?您是否只需要跟踪对 API 的调用以进行审计,并且数据在创建后不会被修改?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-15
    • 2011-01-14
    相关资源
    最近更新 更多