Rails принимает проверки уникальности вложенных атрибутов
У меня есть простая форма, которая позволяет управлять позициями в компании. Я использую accepts_nested_attributes
API для достижения этой цели. Пользователи могут добавлять / удалять позиции с помощью кнопок плюс / минус и выбирать пользователя и позицию для каждого. Единственная проверка, которую я хотел бы применить, заключается в том, что пользователь не может иметь несколько позиций для одной и той же компании. Я применил это так:
class User < ActiveRecord::Base
has_many :positions
end
class Position < ActiveRecord::Base
belongs_to :user
belongs_to :company
validates :title, presence: true
validates :user, presence: true
validates :company, presence: :true
validates :user, uniqueness: { scope: :company }
end
class Company < ActiveRecord::Base
has_many :positions
accepts_nested_attributes_for :positions, allow_destroy: true
end
Тем не менее, я заметил ошибку, если человека удаляют, а затем снова добавляют в ту же компанию без сохранения между ними. Это пример иллюстрирует проблему:
company = Company.create(name: "Widgets")
mark = User.create(name: "Mark")
luke = User.create(name: "Luke")
mark_position = Position.create(company: company, user: mark, title: "CTO")
luke_position = Position.create(company: company, user: luke, title: "CFO")
company.positions_attributes = [
{ id: mark_position.id, _destroy: true },
{ id: john_position.id, _destroy: true },
{ user_id: mark.id, title: "CPO" },
{ user_id: john.id, title: "CMO" },
]
company.save!
Проверка не удалась: позиционирует дубликат пользователя в компании для пользователя
Могу ли я что-либо сделать, чтобы изменения, подобные этим, не приводили к сбою проверок назначений (я также применяю принудительно на уровне базы данных - это означает, что на сервере возникают ошибки 5xx)?
1 ответ
Хотя это не красиво - вы можете переопределить positions_attributes=
чтобы переназначить ваши данные:
def positions_attributes=(attributes)
existing = Array(positions)
attributes.each do |iteration|
position = existing.find { |position| position.user_id.eql?(iteration[:user_id]) }
if position
attributes.select{ |iteration| iteration[:id].eql?(position.id) }.each{ |iteration| iteration.delete(:id) }
iteration[:id] = position.id
end
end
super attributes
end