【问题标题】:How is this validation error possible for this code?此代码如何可能出现此验证错误?
【发布时间】:2018-03-02 03:25:40
【问题描述】:

错误是ActiveRecord::RecordInvalid: Validation failed: Route must exist

这是代码:

new_route = Route.create!(new_route_data)

new_points.each_with_index do |point, index|
  new_point_data = { route_id: new_route.id,
                     latitude: point[0],
                     longitude: point[1],
                     timestamp: point[2] }
  new_point = Point.create!(new_point_data)
end

正在报告new_point = Point.create!(new_point_data) 行。

相关细节:

  • 正如您在上面看到的那样,此代码在单个 Sidekiq 工作器中运行(因此,路径不是在一个工作器中创建,而是在另一个工作器中创建点 - 这都是内联的)
  • routes 表有近 3M 条记录
  • points 表有大约 2.5B 条记录
  • Point 模型包含 belongs_to :route, counter_cache: true
  • Route 模型没有验证
  • 如果相关,Route 模型确实包含 belongs_to :user, counter_cache: true
  • users 表中只有大约 5k 条记录

软件版本:

  • Rails 5.1.5
  • Ruby 2.5.0
  • PostgreSQL 9.6.7

【问题讨论】:

  • 你需要做这个作业吗? new_point = Point.create!(new_point_data)... 你能尝试只做Point.create!(new_point_data) 并在new_point_data 中设置route: new_route 而不是route_id: new_route.id
  • 是的,我赞同上述观点。如果您在Point 中的验证是validates :route, presence: true,那么您应该将属性传递为route: new_route 而不是route_id: new_route.id,因为验证是在route 上,而不是route_id
  • 我将尝试直接引用 activity 而不是 activity.id 并在监视一下(可能需要几天)后在这里跟进。我稍后会使用new_point.id,这就是为什么我(我认为我)需要做这个任务。
  • 可能是 Sidekiq 太快了,new_route 在尝试访问它之前还没有提交到数据库。您可能需要确保 new_route.id 在分配之前可用。
  • @EJ2015 这真的是 sidekiq 太快了吗?在这一点上,它只是 ruby​​ 代码——所有这些都发生在一个进程中。如果new_route 尚未提交到数据库,我难道不希望这些错误任何地方出现在我的 Rails 应用程序中吗?

标签: ruby-on-rails ruby activerecord


【解决方案1】:

首先,您的代码没有意义。您正在迭代 new_point 并将 new_point 分配给块中的某个值。所以我假设你的意思是对一些名为 data_points 的集合进行迭代

试试这个。

在路线模型中

has_many :points

然后:

new_route = ...
data_points.each do |point|
  point_data = {latitude: point[0], ...}   # do not include route_id
  new_route.points.create(point_data) # idiomatic
end

` 你不需要索引,所以不要使用 each_with_index。

【讨论】:

  • 糟糕!你是对的 - 我现在正在修复 each 调用......我正在迭代 new_points 以创建单独的 new_point 对象......
  • Aghk - 我 am 在我的真实代码中也使用了index。这是一个非常不稳定的数据结构,我不想在这里讨论......抱歉造成额外的混乱!
  • 您的主要观点是从Point.create! 切换到new_route.points.create(因为我已经在Route 模型上拥有has_many)...现在我对两者的区别……
【解决方案2】:

如果不查看您在 Point 模型中的验证类型,就很难说出问题所在。

我的猜测是您在 point.rb 中的验证是:

validates :route, presence: true

使用 ActiveRecord 关系,您可以使用此快捷方式来避免显式分配 route_id:

new_point_data = { latitude: point[0],longitude: point[1], timestamp: point[2] }
new_route.points.create!(new_point_data)

new_point 数据没有 route_id。

您还应该重命名您在块中分配的 new_point,因为您正在覆盖您正在迭代的数组。

【讨论】:

  • Point 模型上没有验证。
猜你喜欢
  • 1970-01-01
  • 2020-07-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-23
  • 2017-10-16
  • 2020-12-03
  • 2018-07-15
相关资源
最近更新 更多