Rails:保存后验证关联?

Pla*_*Ton 9 validation ruby-on-rails

我有一个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
  # ...
Run Code Online (Sandbox Code Playgroud)

我能想到绕过这个问题的唯一方法包括禁用我不想做的验证,或者双重保存到DB,这不是完全有效的.

处理此问题的标准方法是什么?

Chr*_*erg 10

经过一番研究,这个解决方案似乎最简单.首先,在您的Role模型中,而不是验证user_id,验证user:

validates :user, :presence => true
Run Code Online (Sandbox Code Playgroud)

然后,在您的User模型中,添加:inverse_of => :user到您的has_many通话中:

has_many :roles, :inverse_of => :user
Run Code Online (Sandbox Code Playgroud)

然后它按预期工作:

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">
Run Code Online (Sandbox Code Playgroud)

但请注意,这仍会产生两个SQL事务,一个用于保存用户,另一个用于保存角色.我看不出你怎么能避免这种情况.

另请参阅:如何验证与Rails的属于关联的存在?


And*_*bbs 5

如果您将代码更改为如下所示,我认为您可以解决验证问题:

@user = User.new(params[:user])
@user.roles.new(:name => 'Peon') unless @user.has_roles?
if @user.save
  # ...
Run Code Online (Sandbox Code Playgroud)

如果这不起作用,您可以尝试将验证更改为:

class Role < ActiveRecord::Base
  belongs_to :user
  validates :user_id, :presence => true, :unless => Proc.new() {|r| r.user}
end
Run Code Online (Sandbox Code Playgroud)