在Rails JSONB列中使用数组

时间:2019-01-10 08:14:33

标签: ruby-on-rails postgresql activerecord jsonb

我想在JSONB列中存储对象数组。我正在使用Rails 5.2。我正在使用一个自定义序列化程序,以确保分配给JSONB字段的值是一个数组而不是哈希。并且在向字段分配类似[{a: 1}]之类的消息时出现错误。这是代码:
型号

class Printing
  serialize :card_faces, CardFacesSerializer
end

序列化器

class CardFacesSerializer
  include JSONBArraySerializer

  def allowed_attributes
    %i[name image]
  end
end

序列化程序问题

module JSONBArraySerializer
  extend ActiveSupport::Concern

  def initialize(data)
    return [] if data.blank?

    if data.is_a?(String)
      json = Oj.load(data, symbol_keys: true)
    end
    raise ArgumentError, "#{json} must be [{},{}], not {}" if json.is_a?(Hash)
    # Will only set the properties that are allowed
    json.map do |hash|
      hash.slice(self.allowed_attributes)
    end
  end

  class_methods do
    def load(json)
      return [] if json.blank?
      self.new(json)
    end

    def dump(obj)
      # Make sure the type is right.
      if obj.is_a?(self)
        obj.to_json
      else
       raise StandardError, "Expected #{self}, got #{obj.class}"
      end
    end
  end
end

评估时:

pr = Printing.first
pr.card_faces = [{hay: 12}]
pr.save!

我得到一个错误:

  

StandardError:预期的CardFacesSerializer,具有数组

我认为转储/加载的工作方式不清楚。为什么在保存过程中调用dump?如何修复我的代码以使其正常工作?

更新
我设法使其与以下序列化程序代码有关:

module JSONBArraySerializer
  extend ActiveSupport::Concern

  class_methods do
    def load(data)
      return [] if data.blank?

      if data.is_a?(String)
        json = Oj.load(data, symbol_keys: true)
      end
      raise ArgumentError, "#{json} must be [{},{}], not {}" if json.is_a?(Hash)

      # Will only set the properties that are allowed
      json.map do |hash|
        hash.slice(*allowed_attributes)
      end
    end

    def dump(obj)
      # Make sure the type is right.
      if obj.is_a?(Array)
        obj.to_json
      else
       raise ArgumentError, "Expected Array, got #{obj.class}"
      end
    end
  end
end

1 个答案:

答案 0 :(得分:2)

不要将序列化与JSON / JSONB列一起使用。

  

请记住,数据库适配器会处理某些序列化任务   为了你。例如:PostgreSQL中的json和jsonb类型将是   在JSON对象/数组语法和Ruby Hash或Array之间转换   透明对象。在这种情况下,无需使用序列化。   https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html

serialize是一种古老的黑客工具,用于在字符串列中存储JSON / YAML /任何内容。认真-请勿使用。只会导致双重转换的问题。

您应该通过常规的模型验证和/或自定义设置器来处理您的工作。