Преобразование вложенной модели Rails в базу данных и из нее без собственной таблицы - PullRequest
0 голосов
/ 16 декабря 2018

Справочная информация: я пытаюсь реорганизовать свой код после прочтения Практического объектно-ориентированного проектирования в Ruby (это потрясающе), и при этом я хочу представить еще несколько моделей, которые заключают в себе ответственность, а неу меня есть один большой файл с логикой (и операторы case, для этого).

Проблема: Чтобы упростить постановку задачи, у меня есть модель Rule, которая "имеет много" RuleConditions.Однако в базе данных есть только одна таблица для правил.В нем у меня есть столбец для условий типа jsonb (в зависимости от сложности RuleCondition).Но я не могу этого достичь.В частности, Я не могу понять, как создать экземпляр модели с вложенной моделью, и ожидать, что ActiveRecord будет знать, как преобразовать модель в jsonb и, возможно, из таблицы обратно во вложенную модель .Я также не знаю, смогу ли я определить отношение has_many без таблицы, поддерживающей его, используя ActiveRecord.

Что я ожидаю:

Я ожидаю, что должен быть какой-то поток (определенныйсмесью ActiveRecord и ActiveModel), которая сделала бы этот поток возможным

  1. Получить параметры для Rule.
  2. Создать новый массив RuleConditions из подмножестваparams для правила.
  3. Do Rule.new(rule), где правило содержит: условия => RuleCondition
  4. Do rule.save!
  5. Через некоторое время получить правило изи ожидаем, что он восстановит Rule с вложенной моделью RuleConditions из атрибута conditions.

Что я пробовал:

Что я думалПолучил бы меня на полпути был serialize, :conditions, JSON, но он пытается сериализовать мой объект.После этого я действительно не знаю.Я также поиграл с ActiveModel :: Conversion.Поэтому мне просто нужно некоторое руководство.

И, чтобы быть совершенно ясным, вызов as_json на моем RuleCondition работает так, как я ожидал (выводит тот же JSON, который раньше хранился в Rule модель и база данных до попытки рефакторинга).Так что, может быть, я не понимаю serialize (поскольку предполагается, что это YAML, если не указано иное, я думаю, что кодировка отличается от просто "соответствовать моему типу столбца")

Редактировать:

В настоящее время у меня есть что-то вроде (barebones, 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,7 мс) INSERT INTO "rules" ("имя", "условия", "made_at", "updated_at") ЗНАЧЕНИЯ ($ 1, $ 2, $ 3, $ 4) RETURNING "id" [["name", "wefw"], ["условия", "{#}"], ["созданный_ат", "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 веб.1 |КОНТЕКСТ: данные JSON, строка 1: # ... 18:13:52 web.1 |: INSERT INTO "rules" ("имя", "условия", "made_at", "updated_at") ЗНАЧЕНИЯ ($ 1, $ 2, $ 3, $ 4) RETURNING "id" 18:13:52 web.1 |(0,5 мс) ROLLBACK

1 Ответ

0 голосов
/ 16 декабря 2018

Имейте в виду, что адаптеры баз данных выполняют для вас определенные задачи сериализации.Например: типы json и jsonb в PostgreSQL будут преобразованы между синтаксисом объекта / массива JSON и объектами Ruby Hash или Array прозрачно.В этом случае нет необходимости использовать сериализацию.
- api.rubyonrails.org

Не используйте serialize со встроенными столбцами JSON / JSONB.Он предназначен для использования со строковыми столбцами в качестве альтернативы беднякам.

То, что вы пытаетесь сделать, действительно выходит за рамки того, что делает ActiveRecord - AR построен вокруг реляционной модели, где модели соответствуют таблицам.И вы не можете ожидать, что у AR будут какие-либо условия для демонтажа столбца JSONB во что угодно, кроме базовых скалярных типов.И я бы подумал, действительно ли то, что вы делаете, стоит усилий по сравнению с созданием отдельной таблицы для отношения.

Вы на правильном пути с ActiveModel :: Model, которая придаст вашей модели то же поведение, что иобычная модель, но вы должны взглянуть на то, как ActiveRecord обрабатывает вложенные атрибуты :

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

Возможно, вы можете имитировать другие аспекты ассоциации, создавая сеттеры / геттеры.

Но опять же, вы можете просто создать таблицу rule_conditions со столбцом JSONB и ассоциацией один ко многим или м2м и вместо этого тратить свое время на продуктивную работу.

...