Rails: NOT NULL Нарушение: сохранение явно циклических ассоциаций иногда лишено внешних ключей - PullRequest
0 голосов
/ 08 ноября 2019

Использование Ruby-on-Rails 5.2.3 с ruby ​​2.5

Проблема:

Я написал в Rails внешний интерфейс конфигурации сетевого маршрутизатора, и у него есть некоторые циклические определения, напримерВот этот:

  • У переадресации есть много адресов (которые пересылаются).
  • У адреса есть много переадресаций (которые пересылают его).
  • Адресимеет много форвардов (на которые приходят ответы от него).

Добавление: Поскольку это может показаться немного странным на первый взгляд, вот небольшая картинка: enter image description here Можно видеть три различных ассоциации между двумя объектами, где «Адрес» в каждой ассоциации играет совершенно различную роль. (Но они могут менять роли или даже играть несколько функций - и в этом суть приложения: всегда генерируйте синтаксически и логически правильную конфигурацию для конечного устройства - и эта часть уже работает).

Плюс, также необходим obviousely:

  • Адрес принадлежит интерфейсу.

Я построил это обычным способом:

class Forward < ApplicationRecord
  has_many :addresses, dependent: :nullify
  belongs_to :remoteip, class_name: 'Address'
  belongs_to :responding, class_name: 'Address', optional: true
end
class Address < ApplicationRecord
  belongs_to :interface
  belongs_to :forward, optional: true
  has_many :remoteip_forwards, class_name: 'Forward',
           foreign_key: :remoteip_id, dependent: :destroy
  has_many :responding_forwards, class_name: 'Forward',
           foreign_key: :responding_id, dependent: :nullify
end

Тогдав базе данных имеются ограничения, так как объекты не должны быть сиротами, в этом случае:

  • address.interface_id NOT NULL

Тогда у меня есть модель проектирования верхнего уровня, который связывает воедино все остальные модели посредством соответствующих ассоциаций, так что я могу взять один дизайн и получить все вещи как одно дерево.

Пока все это работает просто отлично. Но когда я пытаюсь скопировать весь дизайн (с самоцветом deep_clone), при сохранении некоторые довольно сложных дизайнов дают мне NotNullViolation.

Анализ:

Журналы показывают, что Rails действительно сохраняет некоторые записи Address дважды : он делает INSERT с действительными forward_id, но пустыми interface_id, и только позже это делаетUPDATE для действительного interface_id, все в рамках транзакции. И очевидно, что это снимает ограничение NOT NULL, и действие завершается неудачно (при удалении ограничения копия выполняется успешно).

Я бы понял это, если бы фактический проект действительно содержал циклическую ссылку (потому что тогданет другого способа сохранить его), но нет ни одного ! Только логически возможно создавать циклические ссылки с этими моделями.

Я не смог найти надлежащую документацию о том, как Rails обрабатывает такие (потенциально) циклические вещи. inverse_of ничего не помогает (но этого и следовало ожидать, так как мои имена четко определены).

Когда я использую autosafe: false в проблемных ассоциациях, проблема решается, и Rails получает деревосохранены - но затем записи, на которые не ссылаются дважды, отсутствуют в копии (это не удивительно).

Таким образом, кажется, что код Rails, который обходит дерево ассоциаций для поиска и сохранения записей, не является полностью расширенными слишком рано прибегает к записи записи дважды (возможно, для повышения производительности?).

Как лучше всего к этому подойти?

  • К сожалению, ограничения NOT NULL не являютсяDEFERRABLE в postgresql. Жаль, что это может показаться самым очаровательным решением.
  • использовать autosave=false, пройтись по дереву ассоциаций пешком, чтобы найти правильную последовательность и сохранить каждую запись отдельно? Нет, спасибо.
  • Разметить ассоциации как-то иначе? Как? (У меня уже есть рабочий код, который преобразует все данные для целевого устройства, и он хорошо работает с текущим макетом.)
  • Откажитесь от ограничений NOT NULL? Хм ...
  • Или в Rails есть где-то ручка, которую можно настроить?
...