确保has_many:through关联在创建时是唯一的 [英] Make sure has_many :through association is unique on creation
问题描述
如果在记录创建时保存has_many:through关联,那么如何确保该关联具有唯一的对象。唯一性由一组自定义属性定义。
考虑:
类用户< ActiveRecord :: Base
has_many:user_roles
has_many:roles通过::user_roles
before_validation:ensure_unique_roles
private
def sure_unique_roles
#我认为以下方法会起作用:
self.roles = self.roles.to_a.uniq {| r | #{r.project_id}-#{r.role_id}}
#但是上面的结果重复,而且有点奇怪,因为它通过ActiveRecord赋值运算符进行关联(这很可能是原因它不能正常工作)
#我也尝试过:
self.user_roles = []
self.roles = self.roles.to_a.uniq {| r | #{r.project_id}-#{r.role_id}}
#但这也很奇怪,因为它清除了可能具有辅助数据的用户角色
结尾
end
验证user_roles的最佳方法是什么,角色基于唯一性是唯一的
做到这一点的最佳方法(尤其是在使用关系数据库的情况下)是 user_roles
上的唯一多列索引。
add_index:user_roles,[:user_id ,:role_id],唯一:true
然后在角色添加失败时正常处理:
类用户< ActiveRecord :: Base
def try_add_unique_role(role)
self.roles<<角色
抢救WhateverYourDbUniqueIndexException是
#以某种方式优雅地处理
#(返回false,引发您自己的应用程序异常等)
end
end
关系数据库旨在保证引用完整性,因此可以使用它。任何仅适用于红宝石/铁路的解决方案都将具有竞争条件和/或实际上效率低下。只需检查以下内容即可:
already_has_role = UserRole.exists?(用户:用户,角色:前瞻性角色_additions)
不过,当您尝试保留角色添加时,仍然必须处理潜在的异常。
If you are saving a has_many :through association at record creation time, how can you make sure the association has unique objects. Unique is defined by a custom set of attributes.
Considering:
class User < ActiveRecord::Base
has_many :user_roles
has_many :roles, through: :user_roles
before_validation :ensure_unique_roles
private
def ensure_unique_roles
# I thought the following would work:
self.roles = self.roles.to_a.uniq{|r| "#{r.project_id}-#{r.role_id}" }
# but the above results in duplicate, and is also kind of wonky because it goes through ActiveRecord assignment operator for an association (which is likely the cause of it not working correctly)
# I tried also:
self.user_roles = []
self.roles = self.roles.to_a.uniq{|r| "#{r.project_id}-#{r.role_id}" }
# but this is also wonky because it clears out the user roles which may have auxiliary data associated with them
end
end
What is the best way to validate the user_roles and roles are unique based on arbitrary conditions on an association?
The best way to do this, especially if you're using a relational db, is to create a unique multi-column index on user_roles
.
add_index :user_roles, [:user_id, :role_id], unique: true
And then gracefully handle when the role addition fails:
class User < ActiveRecord::Base
def try_add_unique_role(role)
self.roles << role
rescue WhateverYourDbUniqueIndexExceptionIs
# handle gracefully somehow
# (return false, raise your own application exception, etc, etc)
end
end
Relational DBs are designed to guarantee referential integrity, so use it for exactly that. Any ruby/rails-only solution will have race conditions and/or be really inefficient.
If you want to provide user-friendly messaging and check "just in case", just go ahead and check:
already_has_role = UserRole.exists?(user: user, role: prospective_role_additions)
You'll still have to handle the potential exception when you try to persist role addition, though.
这篇关于确保has_many:through关联在创建时是唯一的的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!