Использование массива в столбце Rails JSONB - PullRequest
0 голосов
/ 10 января 2019

Я хочу хранить массив объектов в столбце JSONB в PostgreSQL. Я использую 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 / load. Почему dump вызывается во время сохранения? Как я могу исправить мой код для правильной работы?

UPDATE
Мне удалось заставить его работать с этим кодом концерна сериализатора:

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 голосов
/ 10 января 2019

Не использовать сериализацию со столбцами JSON / JSONB.

Имейте в виду, что адаптеры баз данных выполняют определенные задачи сериализации для тебя. Например: типы json и jsonb в PostgreSQL будут конвертируется между синтаксисом объекта / массива JSON и хэшем или массивом Ruby объекты прозрачно. В этом случае нет необходимости использовать сериализацию. https://api.rubyonrails.org/classes/ActiveRecord/AttributeMethods/Serialization/ClassMethods.html

serialize - это старый хак, используемый для хранения JSON / YAML / что угодно в строковых столбцах. Серьезно - не используйте это. Это просто приведет к проблемам с двойным преобразованием.

То, что вы делаете, должно вместо этого обрабатываться обычной проверкой модели и / или пользовательским установщиком.

...