Rails has_one构建关联删除现有记录,即使新记录无效



我在Rails(至少5.2.1(中看到,如果你有一个模型与另一个模型有一个has_one关联,就会发生以下情况:

class Car < ApplicationRecord
has_one :steering_wheel
end
class SteeringWheel < ApplicationRecord
belongs_to :car
validate_presence_of :name
end

我有一个带方向盘的现有汽车物体。然后我试着制造一个新的方向盘,就像这样:

car.build_steering_wheel

我尝试构建的新方向盘无效,因为我没有设置name属性。毫无疑问,Rails已经从数据库中删除了我现有的方向盘记录!我理解并依赖于构建关联,在构建新记录时删除现有记录,但在新记录无效时不会删除。

有人知道怎么绕过这个吗?我尝试过在事务中回滚,独立创建方向盘记录,并且只在有效的情况下执行car.steering_wheel=方向盘。。什么都不管用。

ActiveRecord默认情况下不会对关联记录强制执行验证。

你必须使用validates_associated:

class Car < ApplicationRecord
has_one :steering_wheel
validates_associated :steering_wheel
end

irb(main):004:0> Car.create!(steering_wheel: SteeringWheel.new)
(0.3ms)  BEGIN
(0.2ms)  ROLLBACK
ActiveRecord::RecordInvalid: Validation failed: Steering wheel is invalid
from (irb):4

此外,如果您在steering_wheels.car_id上设置了正确的外键,DB将不允许您执行car.build_steering_wheel,因为它会孤立一条记录:

class CreateSteeringWheels < ActiveRecord::Migration[5.2]
def change
create_table :steering_wheels do |t|
t.belongs_to :car, foreign_key: true
t.string :name
t.timestamps
end
end
end

irb(main):005:0> c = Car.create!(steering_wheel: SteeringWheel.new(name: 'foo'))
(0.3ms)  BEGIN
Car Create (0.7ms)  INSERT INTO "cars" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  [["created_at", "2018-11-08 18:53:11.107519"], ["updated_at", "2018-11-08 18:53:11.107519"]]
SteeringWheel Create (2.4ms)  INSERT INTO "steering_wheels" ("car_id", "name", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id"  [["car_id", 3], ["name", "foo"], ["created_at", "2018-11-08 18:53:11.110355"], ["updated_at", "2018-11-08 18:53:11.110355"]]
(1.3ms)  COMMIT
=> #<Car id: 3, created_at: "2018-11-08 18:53:11", updated_at: "2018-11-08 18:53:11">
irb(main):006:0> c.build_steering_wheel
(0.3ms)  BEGIN
(0.6ms)  ROLLBACK
ActiveRecord::RecordNotSaved: Failed to remove the existing associated steering_wheel. The record failed to save after its foreign key was set to nil.
from (irb):6
irb(main):007:0> 

这是has_one关联的build_associated方法的规定功能。build_associated将删除现有关联,而不管正在构建的新关联是否有效。因此,如果在事务过程中有任何情况需要保持旧的关联,则完全不要使用build_associated。

最新更新