我可以覆盖"创建"与" first_or_create"?

时间:2016-02-24 18:16:55

标签: ruby-on-rails ruby ruby-on-rails-4

我目前有SceneRole个型号,以及一个加入它们的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?我相信这会解决我的问题。但是,如果有更好的方法来实现相同的结果,我会感激任何建议。谢谢!

2 个答案:

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