5

我有一个与组有 HABTM 关系的用户模型。我不希望用户能够超过 5 个组,因此想验证 HABTM 关系的长度。

在编辑用户页面上,我有一个复选框列表,用户可以在其中选择他们想要加入的组(我使用 formtastic 作为表单)。

在我的用户控制器中,我正在调用:

@user.update_attributes(params[:user])

这导致rails自动更新关联。

在我的用户模型中,我有以下内容:

def validate
    if self.groups.length > 5
        self.errors.add(:groups, "cannot have more than 5 groups")
    end
end

这导致表单验证失败,但 update_attributes 调用已更新数据库以反映对关联组的更改。这样,每次用户单击保存按钮时,他们的组关联都会保存,即使记录无效。

解决这个问题的最佳方法是什么?

我认为也许验证需要在组模型而不是用户模型上,这行得通吗?理想情况下,我想在不保存记录的情况下更新关联的组,进行验证,然后保存记录。

4

1 回答 1

11

你在这里有两个问题:

  1. 您正在覆盖验证
  2. 保存中的操作顺序引起了问题。

您正在覆盖 validate 方法,这是一件坏事,因为内置行为会拒绝将具有验证错误的记录保存到数据库中。要添加您想要执行的自定义验证:

validate :maximum_group_length

def maximum_group_length
    if self.groups.length > 5
        self.errors.add(:groups, "cannot have more than 5 groups")
    end
end

但是,HABTM 关系的性质要求您将其作为 after_save 回调。只是因为事情完成的顺序。user.groups是基于隐式连接表的,在连接表更新之前不会更新。

如果您尝试将验证作为回调的一部分(before_save、after_creation 等),那么向对象添加错误不会触发回滚。回调只有在返回 false 时才会触发回滚。这将处理问题建议的保存后实施。

after_save :validate_maximum_group_length

def validate_maximum_group_length
    if self.groups.length > 5
        self.errors.add(:groups, "cannot have more than 5 groups")
        return false
    end
end

另一种解决方案是使用显式连接模型。还有一个 has_many :through 关系。连接模型的表在更新语句中更新。has_many :through 和 HABTM 关系在保存后更新关系。

class User < ActiveRecord::Base
  has_many :user_groups
  has_many :groups, :through => user_groups, allow_destroy

  validate :max_group_length
    errors.add(:groups, "cannot have more than 5 groups") if self.user_groups.length > 5
  end

end

class UserGroup < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end

class Group < ActiveRecord::Base
  has_and_belongs_to_many :users
end

HABTM 隐式使用连接表,因此不需要在组端进行更改。

但是,您需要修改表单以更新表单以在 params 哈希中提供 group_id 为params[:user][:user_group_attributes][0][:group_id][3]

于 2009-10-19T21:28:34.653 回答