我有一个与群组有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调用已更新数据库以反映对关联组的更改。这样,每次用户单击保存按钮时,即使记录无效,也会保存其组关联。
解决这个问题的最佳方法是什么?
我认为验证可能需要在群组模型而不是用户模型上,这是否有效?理想情况下,我想更新关联的组而不保存记录,进行验证,然后保存记录。
答案 0 :(得分:11)
你有两个问题:
您正在覆盖验证方法,这是一件坏事,因为内置行为可以将具有验证错误的记录保存到数据库中。要添加自定义验证,请执行以下操作:
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
另一种解决方案是使用显式连接模型。和a_many:通过关系。连接模型的表在update语句中更新。其中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]