ruby-on-rails 强制HABTM的唯一性约束

but5z9lq  于 2022-12-01  发布在  Ruby
关注(0)|答案(1)|浏览(133)

我试图用唯一性约束来管理HABTM关系。
我希望我的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

这就好像关联记得它是“脏的”或未保存的,然后试图保存以前没有保存的记录,最后抛出异常。
有什么想法吗?在哪里可以查看关联是否真的认为它是脏的,和/或如何在捕获异常时重置它的“脏”状态?

bkhjykvo

bkhjykvo1#

ActiveRecord在应用程序层维护数据库中记录的对象表示,包括与其他对象的关系,并尽力使应用程序层数据表示与数据库保持同步。当您将token分配给user时,如下所示:

user.tokens << token

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

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

相关问题