У меня есть модельное отношение, подобное этому:
class Parent < ApplicationRecord
has_one :child, dependent: destroy
end
class Child < ApplicationRecord
belongs_to :parent
end
Внешний ключ находится в таблице childs
:
t.belongs_to :parent, index: { unique: true }, foreign_key: true
В childs_controller
у меня есть create
метод по маршруту POST /parents/<id>/childs
:
def create
@parent = Parent.find(params[:id])
@child = @parent.create_child! params[:child]
render json: @child, status: 200
end
Когда я впервые отправляю данные POST на этот контроллер, дочерняя модель создается соответственно. Когда я снова отправляю дочерние данные в контроллер , он выдает ошибку проверки, так как родительский идентификатор уже занят. Это ожидается. Однако существующий ребенок уничтожается одновременно. Это означает, что когда я отправляю данные в третий раз, создается новый дочерний элемент.
Соответствующий журнал, в котором удален дочерний элемент, выглядит следующим образом:
Child Exists? (0.3ms) SELECT 1 AS one FROM "childs" WHERE "childs"."parent_id" = $1 LIMIT $2 [["parent_id", 1], ["LIMIT", 1]]
(0.2ms) ROLLBACK TO SAVEPOINT active_record_3
Child Load (0.3ms) SELECT "childs".* FROM "childs" WHERE "childs"."parent_id" = $1 LIMIT $2 [["parent_id", 1], ["LIMIT", 1]]
(0.2ms) SAVEPOINT active_record_3
Child Destroy (0.2ms) DELETE FROM "childs" WHERE "childs"."id" = $1 [["id", 1]]
(0.2ms) RELEASE SAVEPOINT active_record_3
ERROR: Validation failed: Parent has already been taken
Я могу обойти это оборачивая создание в транзакцию:
Parent.transaction do
@child = @parent.create_child! params[:child]
end
Но я не понимаю, почему это неожиданно работает.
Итак:
- Почему существующий дочерний элемент Модель, уничтоженная при попытке создать новую (в имеет одно отношение), недопустима?
- Почему обертывание в транзакции предотвращает такое поведение?