Ассоциация сборки Rails has_one удаляет существующую запись, даже если новая запись недействительна - PullRequest
0 голосов
/ 08 ноября 2018

Я видел, что в 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

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

Кто-нибудь знает, как это обойти? Я попытался выполнить откат в транзакции, независимо создавая запись рулевого колеса и только делая car.steering_wheel = штурвал, если он действителен ... ничего не работает.

Ответы [ 2 ]

0 голосов
/ 09 ноября 2018

Это предписанная функциональность метода build_associated для ассоциаций has_one. build_associated удалит существующую ассоциацию независимо от того, действительна ли новая создаваемая ассоциация или нет. Поэтому вообще не используйте build_associated, если во время транзакции возникли обстоятельства, при которых вы хотите сохранить старую связь.

0 голосов
/ 08 ноября 2018

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, БД не разрешит вам сделать 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> 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...