Rails中父模型特定的id列?

时间:2013-02-18 16:23:29

标签: ruby-on-rails ruby database rails-activerecord

考虑以下

:company has_many :issues  #ticket tracking example

我想要路由,公司中的任何用户都可以转到/ issues /:id(使用默认id列时这很简单)。

但是,我希望有一个特定于公司的问题ID(因此每个公司都有自己的问题1,2,3等不是唯一的(并且不会使用内部ID)。

在IssueController中针对该公司ID的创建和更新操作中,根据数据库中的最后一个数字计算id是否有更好的方法? (当每个公司更新/创建多个记录时,我可以在这里考虑种族条件的各种问题。)

提前致谢!

2 个答案:

答案 0 :(得分:1)

我会将issue_id处理降低到模型级别。您可以使用before_validation回调来设置issue_id。仅此一项,即使save调用包含在事务中,也不会阻止竞争条件。您必须通过@PinnyM建议的[ :company_id, :issue_id ]表添加索引/唯一约束来进一步确保这对夫妇issues的唯一性。像这样的东西

class Issue < ActiveRecord::Base
  attr_accessible :company_id, :issue_id
  belongs_to :company
  before_validation :set_issue_id

  private
    def set_issue_id
      self.issue_id = self.company.issues.size + 1
    end
end

class Company < ActiveRecord::Base
  has_many :issues
end

并在迁移中:

add_index :issues, [:issue_id, :company_id], :unique => true

你可以像你说的那样在控制器中抓住正确的问题:

@issue = Issue.where(company_id: current_user.company.id, issue_id: params[:id])

请注意,在实际发生的情况下,这不提供从约束违例异常中恢复的方法。有关如何处理此问题的建议,请参阅@PinnyM答案。

希望这有帮助。

答案 1 :(得分:1)

第一次误解了这个问题之后,我会再拍一次。

您正在寻找一种方法来解决特定于company_id的问题“计数器”(模拟ID)。虽然@deivid在正确的轨道上,依赖issues.count只有在你可以保证公司一次不会发出多个请求的情况下才会起作用。这通常是这种情况,但不能保证,不应单独用作此计数器背后的核心逻辑。

您需要为issues表添加唯一约束/索引 - 这将确保计数器不会重复:

add_index :issues, [:issue_id, :company_id], :unique => true

在模型中添加:uniqueness约束只会通过使竞争条件的窗口更小来缓解问题,但它不能完全保证唯一性。

请注意,如果数据库中存在约束违规,ActiveRecord无法在事务中从中恢复。您可以尝试抢救ActiveRecord::RecordNotUnique,重新创建issue(或者只使用新的issue_id重新生成count)并再次保存。