【问题标题】:Parent model specific id column in Rails?Rails 中的父模型特定 id 列?
【发布时间】:2013-02-03 03:31:44
【问题描述】:

考虑以下

:company has_many :issues  #ticket tracking example

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

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

有没有比在 IssueController 中为该公司 ID 创建和更新操作中基于数据库中的最后一个数字计算 ID 更好的方法? (当每个公司更新/创建多个记录时,我可以在这里想到各种竞争条件问题)。

提前致谢!

【问题讨论】:

  • 所以你想让issue 有一个由“id”和“company_id”组成的复合主键?
  • 不确定让这个工作的最佳方法是什么(尽管我当然可以一起破解一些东西) - 即当请求进入 /issues/5701 时,@issue = Issue.where(company_id: current_user .company.id, issue_id: 参数 [:id])。我更关心这是否是开始在数据库中存储这类东西的最佳方式(以及前面提到的为该公司获取下一个可用 issue_id 的数据库查找感觉很糟糕)。

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


【解决方案1】:

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

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 答案。

希望这会有所帮助。

【讨论】:

  • 这受制于 OP 想要避免的相同竞争条件...issues.size 即使包装在事务中也不保证唯一性。
  • 您能详细说明一下吗?唯一的问题是如果 issues.size 在分配给 issue_id 之后和保存记录之前发生了变化,但这不会发生对吗?
  • 在执行验证时,多个请求可能会产生问题。由于当前的company.issues 集合大小是相同的,即使它们是不同的记录,它们都将被分配相同的“issue_id”。
  • “但这不可能发生,对吧?” - 它肯定会发生。事务不会锁定表以确保不会创建其他记录(除非您使用表锁定),它只是保证事务开始时的数据库“快照”不会在事务上下文中更改。
  • 我想我明白了,谢谢你的解释!将 company_id/issue_id 组合设为唯一可以解决这个问题吗?
【解决方案2】:

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

您正在寻找一种方法来生成特定于 company_id 的问题“计数器”(模拟 id)。尽管@deivid 走在了正确的轨道上,但只有在您能保证一家公司一次不会提出多个请求的情况下,依靠issues.count 才能奏效。这通常是这种情况,但不能保证并且不应单独用作此计数器背后的核心逻辑。

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

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

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

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

【讨论】:

  • 我认为我们应该将两个答案合二为一,因为它们是互补的。您可以将我的(更正的)代码集成到您的代码中,之后我会删除我的。
  • 感谢您的正直,但如果您希望将其合并,您只需将约束信息复制到您的答案中即可。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-12-29
  • 1970-01-01
  • 2021-10-07
  • 1970-01-01
  • 1970-01-01
  • 2016-10-02
  • 2016-06-26
相关资源
最近更新 更多