何时对rails关联的更改会传播到父记录?

时间:2014-09-26 15:48:16

标签: ruby-on-rails activerecord

我有一对rails(4)模型,议程 agenda_items ,以便议程 has_many agenda_items agenda_item belongs_to 议程。很直接。

agenda_item 有两列: position (索引)和 description

我需要交换两个 agenda_items ,所以我交换了两个位置(在 agenda_item 模型中)并保存!这两个记录。当我检查两条记录时,位置已相应更新。

交换后(仍然在模型中)我立即记录议程 agenda_items ,但发现更改 agenda_items 的部分呈现也不会改变。

当我刷新页面时,我发现更改已发生。我曾尝试将交易所交易(无济于事)。我在交换和日志记录之间破解了一个睡眠声明。交换后(但在睡眠期间),我可以看到它已在pgAdmin III中正确完成。

我尝试了Agenda(Item).connection.commit_db_transaction,但两个都向我发出警告,说明没有任何交易处于活动状态。

我在postgresql和sqlite3中尝试了这个,两者都表现出相同的症状。

类似的代码在Rails 2中有效,但直到现在我还没有在3或4中使用它。

非常感谢任何想法/建议。

模型代码(还有很多黑客)是:

def exchange( up )
  delta = up ? -1 : 1
  for item in self.agenda.agenda_items
    Zlogger.debug "before exchange: item #{item.position}:  #{item.description}"
  end
  Zlogger.debug "delta: #{delta}"
  target = self.agenda.agenda_items.where(['position = ?',self.position + delta]).first
  Zlogger.debug "exchanging #{self.position} #{self.description} #{target.position} #{target.description}"  
  p = self.position
  # moving the target to position 0, later moving it to its destination
  # is a hack to make sure that the moves are committed immediately (doesn't always work)
  ActiveRecord::Base.transaction do
    target.position = 0
    target.save!
    self.position = self.position + delta
    self.save!
    target.position = p
    target.save!
    self.agenda.save!
  end
  Zlogger.debug "exchanged #{self.position} #{self.description} #{target.position} #{target.description}"
  sleep 30.0
  for item in self.agenda.agenda_items
    Zlogger.debug "after exchange: item #{item.position}:  #{item.description}"
  end
end

Zlogger的典型结果:

before exchange: item 1:  Treasurer''s Report
before exchange: item 2:  Approval of Last Month''s Minutes
before exchange: item 3:  Permanent Layout Report
before exchange: item 4:  Sectional Layout Report
before exchange: item 5:  HO Layout Report
before exchange: item 6:  Operating Committee Report
before exchange: item 7:  Membership Promotion and Engagement
before exchange: item 8:  New Business
before exchange: item 9:  Adjournment
delta: 1
*** Note that the exchange does take place ***
exchanging 1 Treasurer''s Report 2 Approval of Last Month''s Minutes
exchanged 2 Treasurer''s Report 1 Approval of Last Month''s Minutes
*** Note that the exchange has not propagated to the association ***
after exchange: item 1:  Treasurer''s Report
after exchange: item 2:  Approval of Last Month''s Minutes
after exchange: item 3:  Permanent Layout Report
after exchange: item 4:  Sectional Layout Report
after exchange: item 5:  HO Layout Report
after exchange: item 6:  Operating Committee Report
after exchange: item 7:  Membership Promotion and Engagement
after exchange: item 8:  New Business
after exchange: item 9:  Adjournment

1 个答案:

答案 0 :(得分:0)

我修好了!我一直在挖掘,经过长时间的谷歌会议后,我发现rails会缓存一个活跃的记录关联。当我更改相关项的内容时,我会认为缓存将无效,因此下一次引用该关联将获得新值,但它不是。

有两种方法可用于解决此问题:

reset - >如果关联已更改,则重新加载关联。 根据rails文档,这是首选方法 - 但它不起作用!

reload - >即使不需要重新加载,也会强制关联重新加载。 这有效!

就我而言,由于已知前面的语句已经更改了关联的内容,因此保证需要reload,因此reset有点多余。

在删除所有调试和黑客之后,代码很简单:

def exchange( up )
  delta = up ? -1 : 1
  target = self.agenda.agenda_items.where(['position = ?',self.position + delta]).first
  target.position = self.position
  self.position = self.position + delta
  target.save!
  self.save!
  self.agenda.agenda_items.reload # makes agenda_items consistent with updated contents.
end

我不知道这是否是rails中的错误,但正如我在我的问题中所说,我在Rails 2中使用了类似的代码 - 但不需要resetreload IIRC。 rails文档还暗示reset应检测到内容已被修改的事实,并仅在需要时调用reload