Rails关联缓存问题

时间:2012-12-18 10:08:30

标签: ruby-on-rails associations

我有以下模型/关联设置:

class Contact < ActiveRecord::Base
  has_and_belongs_to_many :assigned_users, class_name: 'User'

  def auto_assign_team_member(project)
    team = project.teams(true).order('last_assigned_contact_at ASC').first
    if team && (user = team.user)
      self.assigned_users << user
      team.update_attribute(:last_assigned_contact_at, Time.now)
    end
  end
end

class Team < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
end

class User < ActiveRecord::Base
  has_many :teams
  has_many :projects, through: :teams
  has_and_belongs_to_many :assigned_contacts, class_name: 'Contact'
end

class Project < ActiveRecord::Base
  has_many :teams
  has_many :users, through: :teams
end

我还有一组Contact个实例,我遍历并执行auto_assign_team_member方法。我的问题是team lvar每次执行都会为几乎返回相同的团队。我在teams表中有两条记录,我的目的是将团队成员均匀地分配到Contact#assigned_users关联中。

在控制台中,它可以工作:

project.teams(true).order('last_assigned_contact_at ASC').first #=> Team(user_id: 1)
project.teams(true).order('last_assigned_contact_at ASC').first.update_attribute(:last_assigned_contact_at, Time.now)
project.teams(true).order('last_assigned_contact_at ASC').first #=> Team(user_id: 2)

知道我哪里错了吗?

编辑如果有人有兴趣,我创建了一个小应用程序来重现这个https://github.com/injekt/teamr

您可以简单地运行rake db:seed来运行测试数据,输出应均匀分布在每个用户上,而不是每次都显示相同的用户。

编辑事实证明,做update_attribute(:last_assigned_contact_at, 'now()')代替正常。也许Rails在引擎盖下做了一些时髦的事情?我意识到这需要高精度,但我认为rails可以处理

1 个答案:

答案 0 :(得分:2)

似乎问题是Rails从任何Time对象中舍入usec,可能是因为只有PostgreSQL实际上支持那种“没有时区的时间戳”数据类型的精度。

时间戳属性的处理方式与其他属性不同,此模块为其定义自定义设置器:https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb

在比较和分配值https://github.com/rails/rails/blob/3-2-stable/activerecord/lib/active_record/attribute_methods/time_zone_conversion.rb#L46

之前,时间对象会被舍入

奇怪的问题是,对于内置的Rails时间戳列,精度就在那里。 这是因为内置时间戳通过调用write_attribute(:column, value)来绕过自定义设置器,team.update_attribute只是平坦地插入整个时间值,以及任何usec。

因此修复此问题而不是team.send(:write_attribute, :last_assigned_contact_at, Time.current) team.save 调用

{{1}}

这有效地绕过了整个自定义setter逻辑并处理了值,就像处理内置时间戳更新一样。