我的User
模型有很多roles
。角色包含user_id
字段,我想validate_presence_of
问题是:如果我在创建时为用户分配了一个角色,则验证失败,因为没有设置user_id。现在,我做想要验证user_id是否存在,但我需要在检查之前保存用户。
目前代码如下:
@user = User.new(params[:user])
@user.roles << Role.new(:name => 'Peon') unless @user.has_roles?
if @user.save
# ...
我能想到解决问题的唯一方法包括禁用我不想做的验证,或者双重保存到DB,这不是完全有效的。
处理此问题的标准方法是什么?
答案 0 :(得分:9)
经过一番研究,这个解决方案似乎最简单。首先,在Role
模型中,验证user_id
,而不是验证user
:
validates :user, :presence => true
然后,在您的User
模型中,将:inverse_of => :user
添加到has_many
来电:
has_many :roles, :inverse_of => :user
然后按预期工作:
irb(main):001:0> @user = User.new
=> #<User id: nil, created_at: nil, updated_at: nil>
irb(main):002:0> @user.roles << Role.new(:name => "blah")
=> [#<Role id: nil, user_id: nil, name: "blah", created_at: nil, updated_at: nil>]
irb(main):003:0> @user.roles[0].user
=> #<User id: nil, created_at: nil, updated_at: nil>
irb(main):004:0> @user.save
(0.1ms) begin transaction
SQL (3.3ms) INSERT INTO "users" ("created_at", "updated_at") VALUES (?, ?) [["created_at", Fri, 04 Jan 2013 02:29:33 UTC +00:00], ["updated_at", Fri, 04 Jan 2013 02:29:33 UTC +00:00]]
User Load (0.1ms) SELECT "users".* FROM "users" WHERE "users"."id" = 3 LIMIT 1
SQL (0.2ms) INSERT INTO "roles" ("created_at", "name", "updated_at", "user_id") VALUES (?, ?, ?, ?) [["created_at", Fri, 04 Jan 2013 02:29:34 UTC +00:00], ["name", "blah"], ["updated_at", Fri, 04 Jan 2013 02:29:34 UTC +00:00], ["user_id", 3]]
(1.9ms) commit transaction
=> true
irb(main):005:0> @user.roles.first
=> #<Role id: 4, user_id: 3, name: "blah", created_at: "2013-01-04 02:29:34", updated_at: "2013-01-04 02:29:34">
但请注意,这仍会产生两个SQL事务,一个用于保存用户,另一个用于保存角色。我不知道你怎么能避免这种情况。
另请参阅:How can you validate the presence of a belongs to association with Rails?
答案 1 :(得分:5)
如果您将代码更改为以下内容,我认为您可以解决验证问题:
@user = User.new(params[:user])
@user.roles.new(:name => 'Peon') unless @user.has_roles?
if @user.save
# ...
如果这不起作用,您可以尝试将验证更改为:
class Role < ActiveRecord::Base
belongs_to :user
validates :user_id, :presence => true, :unless => Proc.new() {|r| r.user}
end
答案 2 :(得分:3)
您必须查看ActiveRecord's Callbacks。您可能会使用before_validation
来执行此操作。
答案 3 :(得分:1)
对于搜索has_many :through
关联此问题的解决方案的任何人,截至2013年12月5日,:inverse_of
选项不能与:through
一起使用({ {3}})。相反,您可以使用@ waldyr.ar建议的方法。例如,如果我们的模型设置如下:
class User < ActiveRecord::Base
has_many :roles
has_many :tasks, through: roles
end
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :task
end
class Task < ActiveRecord::Base
has_many :roles
has_many :users, through: roles
end
我们可以按照以下方式修改我们的Role
课程,以便在保存之前验证task
和user
的存在
class Role < ActiveRecord::Base
belongs_to :user
belongs_to :task
before_save { validates_presence_of :user, :task }
end
现在,如果我们创建一个新的User
并添加一对tasks
,就像这样:
>> u = User.new
>> 2.times { u.tasks << Task.new }
正在运行u.save
将保存User
和Task
,并透明地构建并保存新的Role
,其外键user_id
和{{ 1}}设置得恰当。验证将针对所有模型运行,我们可以继续我们的快乐方式!