【问题标题】:Enforcing a uniqueness constraint for HABTM对 HABTM 实施唯一性约束
【发布时间】:2022-12-11 11:28:00
【问题描述】:

我正在尝试使用唯一性约束来管理 HABTM 关系。

IE。我想让我的User

has_and_belongs_to_many :tokens

但我不希望同一令牌多次与给定用户相关联。

我在连接表上放了一个唯一索引

add_index users_tokens [:user_id, :token_id], unique: true

如果代码尝试多次向给定用户添加相同的令牌,这会正确地导致抛出 ActiveRecord::RecordNotUnique 异常。

在我的代码中,我希望只是默默地捕获/吞下这个异常,就像这样:

begin
    user << token 
rescue ActiveRecord::RecordNotUnique
    # nothing to do here since the user already has the token
end

但是,我遇到了一个问题,当我的用户对象被修改为其他内容时,我的代码中稍后会抛出 RecordNotUnique 异常。

所以一些代码调用类似

...
# The following line throws ActiveRecord::RecordNotUnique
# for user_tokens, even though 
# we are not doing anything with tokens here:
user.update_counters

就好像协会记得它是“脏的”或未保存的,然后尝试保存之前没有保存的记录,并最终抛出异常。

有什么想法可以查看关联是否真的认为它是脏的,和/或当我捕获到异常时如何重置其“脏”状态?

【问题讨论】:

  • 这个问题不是很清楚。我认为您需要阐明您希望实现的目标,并向我们展示未按照您的预期执行的代码。您是否尝试将非唯一的 Token 添加到 User 并且您是否使用 &lt;&lt; 运算符来执行此操作?当你这样做时,你期望发生什么?
  • 谢谢@Toby1Kenobi - 让我知道这是否感觉更清楚一点?
  • 您是否有镜像数据库约束的验证?让 Rails 在只响应数据库错误之前通过验证更早地发现问题。
  • @hbhanoo 谢谢,虽然user &lt;&lt; token 更好,但我想你的意思是user.tokens &lt;&lt; already_connected_token

标签: ruby-on-rails activerecord


【解决方案1】:

ActiveRecord 在应用层维护数据库中记录的对象表示,包括与其他对象的关系,并努力保持应用层数据表示与数据库同步。当您像这样将 token 分配给 user 时:

user.tokens << token

首先 ActiveRecord 查找任何会阻止分配的应用程序级验证,没有找到它将 token 链接到应用程序层中的 user,然后它继续发出必要的 DB 请求以在数据库层。数据库有一个阻止它的约束,因此会引发错误。您从错误中解救并继续,但两个对象的应用程序级连接仍然存在。下次您通过 ActiveRecord 对同一个 user 对象进行任何编辑时,它将再次尝试使数据库与对象在应用程序中的表示方式同步,并且由于与 token 的连接仍然存在将再次尝试在数据库中插入此连接,但这次无法挽救出现的错误。

因此,当您从数据库错误中解救出来时,您还必须像这样撤消应用程序级别的更改:

begin
    user.toekns << token 
rescue ActiveRecord::RecordNotUnique
    user.tokens.delete(token)
end

【讨论】:

  • 实际上,我很确定我尝试过这个,它最终发出了一个数据库查询,该查询也删除了现有的用户 >-< 令牌关系。这就是为什么我试图查看 proxy_object 或类似的东西,看看我是否可以从那里删除记录。我想知道是否切换到 has_many :through 是去这里的方法。
  • 切换到has_many :through 是个好主意。我很早就学会了避开 HABTM,现在再也不靠近它了。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-04
  • 2017-06-13
相关资源
最近更新 更多