【发布时间】:2020-10-12 22:56:19
【问题描述】:
我有一个具有以下关联的任务模型:
...
# === missions.rb (model) ===
has_many :addresses, as: :addressable, dependent: :destroy
accepts_nested_attributes_for :addresses
has_many :phones, as: :phoneable, dependent: :destroy
accepts_nested_attributes_for :phones
has_one :camera_spec, as: :camerable, dependent: :destroy
accepts_nested_attributes_for :camera_spec
has_one :drone_spec, as: :droneable, dependent: :destroy
accepts_nested_attributes_for :drone_spec
...
当用户创建任务时,他们将任务、电话、地址、CameraSpec 和 DroneSpec 的所有信息输入到一个大表单中。当所有信息都正确时,我能够正确保存记录。但是,如果任何模型中出现错误,我想回滚 ALL 事务并呈现有错误的表单。
此主题已在其他地方讨论过,但是,我无法使用我见过的方法回滚所有事务。目前,如果其中一个模型(例如 CameraSpec)出现 DB/ActiveRecord 错误,则之前创建的 Mission、Address 和 Phone 不会回滚。我尝试过嵌套事务,例如:
Mission.transaction do
begin
# Create the mission
Mission.create(mission_params)
# Create the Address
raise ActiveRecord::Rollback unless Address.transaction(requires_new: true) do
Address.create(address_params)
raise ActiveRecord::Rollback
end
...
rescue ActiveRecord::Rollback => e
...
end
end
我尝试抛出不同类型的错误,例如ActiveRecord::Rollback。我总是能够捕捉到错误,但数据库不会回滚。我已经尝试过使用和不使用 begin-rescue 语句。我目前的尝试是不嵌套事务,而是将它们提交到单个事务块中,但这也行不通。这是我当前的代码。
# === missions_controller.rb ===
def create
# Authorize the user
# Prepare records to be saved using form data
mission_params = create_params.except(:address, :phone, :camera_spec, :drone_spec)
address_params = create_params[:address]
phone_params = create_params[:phone]
camera_spec_params = create_params[:camera_spec]
drone_spec_params = create_params[:drone_spec]
@mission = Mission.new(mission_params)
@address = Address.new(address_params)
@phone = Phone.new(phone_params)
@camera_spec = CameraSpec.new(camera_spec_params)
@drone_spec = DroneSpec.new(drone_spec_params)
# Try to save the company, phone number, and address
# Rollback all if error on any save
ActiveRecord::Base.transaction do
begin
# Add the current user's id to the mission
@mission.assign_attributes({
user_id: current_user.id
})
# Try to save the Mission
unless @mission.save!
raise ActiveRecord::Rollback, @mission.errors.full_messages
end
# Add the mission id to the address
@address.assign_attributes({
addressable_id: @mission.id,
addressable_type: "Mission",
address_type_id: AddressType.get_id_by_slug("takeoff")
})
# Try to save any Addresses
unless @address.save!
raise ActiveRecord::Rollback, @address.errors.full_messages
end
# Add the mission id to the phone number
@phone.assign_attributes({
phoneable_id: @mission.id,
phoneable_type: "Mission",
phone_type_id: PhoneType.get_id_by_slug("mobile")
})
# Try to save the phone
unless @phone.save!
raise ActiveRecord::Rollback, @phone.errors.full_messages
end
# Add the mission id to the CameraSpecs
@camera_spec.assign_attributes({
camerable_id: @mission.id,
camerable_type: "Mission"
})
# Try to save any CameraSpecs
unless @camera_spec.save!
raise ActiveRecord::Rollback, @camera_spec.errors.full_messages
end
# Add the mission id to the DroneSpecs
@drone_spec.assign_attributes({
droneable_id: @mission.id,
droneable_type: "Mission"
})
# Try to save any DroneSpecs
unless @drone_spec.save!
raise ActiveRecord::Rollback, @drone_spec.errors.full_messages
end
# If something goes wrong, render :new again
# rescue ActiveRecord::Rollback => e
rescue => e
# Ensure validation messages exist on each instance variable
@user = current_user
@addresses = @user.addresses
@phones = @user.phones
@mission.valid?
@address.valid?
@phone.valid?
@camera_spec.valid?
@drone_spec.valid?
render :new and return
else
# Everything is good, so redirect to the show page
redirect_to mission_path(@mission), notice: t(".mission_created")
end
end
end
【问题讨论】:
标签: ruby-on-rails ruby database activerecord transactions