即使在事务回滚 [closed] 后,也会保存 Rails has_one关联

Rails has_one association is saved even after the transaction is rollback [closed]

提问人:Sanjay Salunkhe 提问时间:9/4/2023 最后编辑:Sanjay Salunkhe 更新时间:9/7/2023 访问量:80

问:


编辑问题以包括所需的行为、特定问题或错误以及重现问题所需的最短代码。这将帮助其他人回答这个问题。

3个月前关闭。

我正在将 rails 3 应用程序升级到 rails 5 版本。我有has_one和belongs_to协会。我正在交易块中保存记录。在 Rails 5 中,我观察到,如果事务被回滚,则空数据会入到“任务”表中。在 rails 3 的情况下,这不会发生。这是 Rails 5 中的预期行为吗?我不想破坏现有的功能,那么有没有办法在 Rails 5 中保留 Rails 3 的行为?

下面是产生此问题的示例代码。

class Student < ApplicationRecord
    has_one :task
end

class Task < ApplicationRecord
    belongs_to :student
    validates_presence_of :name
end

student = Student.first
student.name = nil
def student.testing
 begin
  ActiveRecord::Base.transaction do
   t = Task.new
   t.name = 'ddd'
   t.student = self
   t.save!
   puts "raising exception"
   raise "exception"
  end
 rescue => e
 end
end
student.testing
student.save!
Ruby-on-Rails 红宝石 Ruby-on-Rails-5

评论

1赞 max 9/4/2023
用于在模型的单个实例上定义方法非常奇怪。我会将该方法移动到类定义中,以使此代码不那么容易引起读者的混淆。在没有特定错误类的情况下使用也是口袋妖怪异常处理反模式的一个例子,也是对意外行为的极有可能的解释。defrescue
1赞 mechnicov 9/4/2023
您的问题不清楚。保存has_one关联并将空数据插入到belongs_to表中是什么?请用易于理解的例子更新您的问题
0赞 Sanjay Salunkhe 9/4/2023
@max我不明白的是,当我将关系从 has_one 更改为 has_many 时,为什么不会发生这种情况:任务
0赞 Sanjay Salunkhe 9/4/2023
@Max我发布的代码只是为了重现错误。.实际代码是不同的。我想知道为什么当我更改与has_many的关系时没有插入空数据
0赞 spickermann 9/4/2023
我不清楚你的问题。“插入空数据”是什么意思?究竟什么是空的并保存到数据库中,学生(不能为空,因为它似乎是有效的)。或者(这完全有意义,因为分配的任务在数据库级别回滚)?Rails 3 有什么不同,如果它不写空任务,它写了什么值?我不清楚你期望的输出会是什么样子。student.task

答:

0赞 smathy 9/4/2023 #1

假设“belongs_to表”是(为什么不在这里只说“学生”或“任务”?)那么是的,你显示的代码应该会导致“空数据”被保存到表中。studentsstudents

不知道为什么你认为代码周围的回滚事务块会影响代码?Task#save!student.name = nil; student.save!

评论

0赞 Sanjay Salunkhe 9/4/2023
他们为什么在我更改与has_many的关系时没有插入空记录?为什么只有在has_one的情况下才会插入此 null 记录?
0赞 smathy 9/5/2023
你的问题是关于 Rails 3 和 5 的,如果你想问一些关于 vs 的问题,那么你需要更新你的问题,这样我们才能看到你在说什么。has_onehas_many
1赞 Alex 9/4/2023 #2

从这个例子中,我只能找出一个问题——那就是 rails 如何检测关联。inverse_of

有一个

class Task < ApplicationRecord
  belongs_to :student
end

class Student < ApplicationRecord
  has_one :task, dependent: :destroy
end
>> Task.reflect_on_association(:student).inverse_of.name
=> :task

因为inverse_of是设置的,所以当你赋值给逆时也会赋值 - :studenttask.studentstudent.task

student = Student.first

student.association(:task).target    #=> nil
Task.new(student: student)
student.association(:task).target    #=> #<Task:0x00007f2333a18ad0 id: nil, name: nil, student_id: 2>

# something to save ---------------------^
student.save!

# INSERT INTO "tasks" ("name", "student_id") VALUES (?, ?)  [["name", nil], ["student_id", 2]]

如果回滚任务事务,对象仍分配给 ,并且在保存父级时保存新关联,这并不重要。这做了完全相同的事情:student.task

student = Student.first
Task.transaction do
  Task.create!(student: student)
  raise ActiveRecord::Rollback
end
student.save!

TRANSACTION (0.1ms)  begin transaction
Task Create (0.2ms)  INSERT INTO "tasks" ("name", "student_id") VALUES (?, ?)  [["name", nil], ["student_id", 2]]
TRANSACTION (0.1ms)  rollback transaction
TRANSACTION (0.0ms)  begin transaction
Task Create (0.1ms)  INSERT INTO "tasks" ("name", "student_id") VALUES (?, ?)  [["name", nil], ["student_id", 2]]
TRANSACTION (9.8ms)  commit transaction

无逆向

class Task < ApplicationRecord
  belongs_to :student, inverse_of: false
end

行为与以下方面相同:has_many

Task.reflect_on_association(:student).inverse_of #=> nil
student = Student.first

student.association(:task).target    #=> nil
Task.new(student: student)
student.association(:task).target    #=> nil

# nothing to save -----------------------^
student.save!

有很多

class Task < ApplicationRecord
  belongs_to :student
end

class Student < ApplicationRecord
  has_many :tasks, dependent: :destroy
end
Task.reflect_on_association(:student).inverse_of # => nil
student = Student.first

student.association(:tasks).target    #=> []
Task.new(student: student)
student.association(:tasks).target    #=> []

# nothing to save ------------------------^
student.save!

与反向

class Task < ApplicationRecord
  belongs_to :student, inverse_of: :tasks
end
Task.reflect_on_association(:student).inverse_of.name #=> :tasks
student = Student.first

student.association(:tasks).target    #=> []
Task.new(student: student)
student.association(:tasks).target    #=> [#<Task:0x00007f007d82b2c8 id: nil, name: nil, student_id: 2>]

# something to save ----------------------^
student.save!

# INSERT INTO "tasks" ("name", "student_id") VALUES (?, ?)  [["name", nil], ["student_id", 2]]