Ruby on rails 如何知道何时所有关联都有一个特定的属性?

Ruby on rails 如何知道何时所有关联都有一个特定的属性?,ruby-on-rails,associations,Ruby On Rails,Associations,我有以下协会: Order has_many Row Row belongs_to RowStatus 我希望Order能够知道其所有关联行何时具有特定状态。因此,在行模型中,我有以下回调: before_save bubble_up_status def bubble_up_status if self.row_status_changed? self.order.n_reviewed_rows += 1 if self.row_status.name == 'reviewed

我有以下协会:

Order has_many Row
Row belongs_to RowStatus
我希望Order能够知道其所有关联行何时具有特定状态。因此,在行模型中,我有以下回调:

before_save bubble_up_status

def bubble_up_status
  if self.row_status_changed?
    self.order.n_reviewed_rows += 1 if self.row_status.name == 'reviewed'
    self.order.n_failed_rows += 1 if RowStatus::failed_names.include?(self.row_status.name)
    self.order.n_pending_review_rows +=1 if self.row_status.name == 'pending_review'
    self.order.check_all_statuses
  end
end
然后在我的订单模型中

def check_all_statuses
  if n_reviewed_rows == rows_count
    update(order_status: OrderStatus.find_by(name: 'reviewed'))
  end
  if n_failed_rows == rows_count
    update(order_status: OrderStatus.find_by(name: 'failed'))
  end
  if n_pending_review_rows == rows_count
    update(order_status: OrderStatus.find_by(name: 'pending_review'))
  end
end
当我在before_保存时不断更新父模型(Order)中的属性时,我发现这个问题,因此可能出现误报


我可以使用哪种模式来解决此问题?

只需查找rowstatus不等于所需rowstatus的所有订单。如果有任何行没有该行状态,那么您就知道所有行都没有该行状态

ideal_status = RowStatus.find_by(name: 'Completed')
order.rows.where.not(row_status_id: ideal_status.id)
如果您需要查看所有行是否都有几个状态中的一个,只需传递一个所需状态ID数组

ideal_status_ids = RowStatus.where(name: ['Completed', 'Shipped', 'Received']).pluck(:id)
order.rows.where.not(row_status_id: ideal_status_ids)
因此,对于您的情况,如果您想在每一行都“完成”的情况下执行某项操作,您可以使用

if order.rows.where.not(row_status_id: RowStatus.find_by(name: 'Completed').id).empty?
  # do something
end
至于在哪里使用它,我认为保存后的
回调最有意义,因为如果您更改了订单,但由于某种原因保存到行失败,会发生什么情况?此外,如果当前行的rowstatus是所需的rowstatus之一,则您可能只希望在
气泡上升\u status
中进行任何顺序更改,因此您不会在每次保存行时进入数据库检查所有行

class Row < ActiveRecord::Base
  after_save :bubble_up_status

  def bubble_up_status
    ideal_status_ids = RowStatus.where(name: ['Completed', 'Shipped', 'Received']).pluck(:id)
    if ideal_status_ids.include?(row_status.id) && order.rows.where.not(row_status_id: ideal_status_ids).empty?
      # do something
    end
  end
end
运行
rakedb:migrate
后,我们需要调整
顺序
模型,以便它对行计数进行计数器缓存,并对完成的行进行计数。此外,我们需要更新
bubble\u up\u状态
回调以使用新列

class Order < ActiveRecord::Base
  has_many :rows, counter_cache: true
end

class Row < ActiveRecord::Base

  # here I'm using after_update because I'm assuming that
  # new rows can't be completed when first created
  after_update :update_completed

  # Needs to be after update_completed
  after_save :bubble_up_status

  def update_completed
    if persisted?
      if row_status.name == 'Complete' && row_status.name_was != 'Complete'
        order.increment!(:completed_rows_count)
      elsif row_status.name != 'Complete' && row_status.name_was == 'Complete'
        order.decrement!(:completed_rows_count)
      end
    end
  end

  def bubble_up_status
    # Check if all order rows are completed
    if order.rows_count == order.completed_rows_count
      # do something
    end
  end
end
类顺序
附加说明


用一列而不是两列来实现这个逻辑是完全可能的,但我认为使用两列更容易实现和理解。

谢谢您的回答。首先,状态不是字符串,而是一个名为RowStatus的关联,它有一个名为name的属性。第二,当一行状态发生变化时,我如何让订单知道它需要检查所有这些状态?啊,我完全误解了你原来的问题。希望我的最新答案能有所帮助。我现在也正在将所有引用从“状态”更改为“行状态”。再次更新我的答案以包含对“行状态”的引用,而不是“状态”。再次抱歉误读了你的问题。希望这会对你有用。如果没有,请告诉我。这真的很有趣,但假设订单有很多行,那么每次发生后保存时计数不是很昂贵吗?@HommerSmith我添加了一个替代解决方案,该解决方案不会在每次将行状态更改为所需状态时计数。让我知道你的想法!
class Order < ActiveRecord::Base
  has_many :rows, counter_cache: true
end

class Row < ActiveRecord::Base

  # here I'm using after_update because I'm assuming that
  # new rows can't be completed when first created
  after_update :update_completed

  # Needs to be after update_completed
  after_save :bubble_up_status

  def update_completed
    if persisted?
      if row_status.name == 'Complete' && row_status.name_was != 'Complete'
        order.increment!(:completed_rows_count)
      elsif row_status.name != 'Complete' && row_status.name_was == 'Complete'
        order.decrement!(:completed_rows_count)
      end
    end
  end

  def bubble_up_status
    # Check if all order rows are completed
    if order.rows_count == order.completed_rows_count
      # do something
    end
  end
end