11

たくさんある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に二重保存することです。これは正確には効率的ではありません。

この問題を処理するための標準的な方法は何ですか?

4

4 に答える 4

10

少し調べてみると、この解決策が最も簡単なようです。まず、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">

ただし、これでも2つのSQLトランザクションが生成されることに注意してください。1つはユーザーを保存するためのもので、もう1つは役割を保存するためのものです。どうすればそれを回避できるかわかりません。

参照:Railsとの関連付けに属するの存在をどのように検証できますか?

于 2013-01-04T02:33:25.823 に答える
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
于 2013-01-04T02:03:28.743 に答える
3

ActiveRecordのコールバックを確認する必要があります。おそらくあなたはそれをするためにを使うでしょうbefore_validation

于 2013-01-04T02:06:58.683 に答える
1

アソシエーションのこの問題の解決策をグーグルで探している人は、has_many :through2013年12月5日の時点で、このオプションを(ソース:inverse_of)と組み合わせて使用​​することはできません。代わりに、@waldyr.arによって提案されたアプローチを使用できます。たとえば、モデルが次のように設定されている場合::through

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が保存されるだけでなく、外部キーが適切に設定されている新しいものが透過的に作成および保存されます。検証はすべてのモデルで実行され、私たちは楽しい道を進むことができます!UserTaskRoleuser_idtask_id

于 2013-12-05T19:32:26.020 に答える