我目前有Scene
和Role
个型号,以及一个加入它们的SceneRole
关联表:
class Scene < ActiveRecord::Base
has_many :scene_role
has_many :roles, through: :scene_role
end
class Role < ActiveRecord::Base
has_many :scene_role
has_many :scenes, through: :scene_role
end
class SceneRole < ActiveRecord::Base
belongs_to :scene
belongs_to :role
validates_presence_of :scene_id
validates_presence_of :role_id
validates_uniqueness_of :scene, :scope => :role
end
我想确保任何相同的场景 - 角色关系都是唯一的。但是,当关系已经存在且没有出现错误时,我还希望优雅地处理将Role
添加到Scene
的尝试:ActiveRecord::RecordInvalid: Validation failed: Scene has already been taken
我的测试代码:
role = Role.new(name: "Big Bossman")
scene = Scene.new(name: "Arena")
scene.roles << role # success
scene.roles << role # Exception
是否可以使用create
的行为覆盖first_or_create
?我相信这会解决我的问题。但是,如果有更好的方法来实现相同的结果,我会感激任何建议。谢谢!
答案 0 :(得分:2)
可以使用monkey patching,但这是一个非常糟糕的主意。当您或其他人希望代码在某个默认情况下运行时,您会遇到重大问题办法。如果需求发生变化并删除验证怎么办?您将默默地无法使用create
创建多个记录,因为first_or_create
始终会找到现有记录。
最好的选择是检查role
中是否已存在scene.roles
。例如:
scene.roles.include?(role) ? false : scene.roles << role
# does scene.roles include role? if yes: do nothing, if not: add role
或做this之类的事情。
答案 1 :(得分:0)
一种方法是在before_validation
方法中添加此方法,该方法实际上不会引发错误,但会删除之前的角色(如果存在)。在这里你可以处理边缘情况。
class SceneRole < ActiveRecord::Base
before_validation :single_relation
def single_relation
# Checks new and stored records for duplicate and removes it
# Might want to have this on both scene and role instead of here
end
end