2016-05-05 8 views
2

私は、と呼ばれる結合モデルとの関連がself referential has-many throughであるTemplateと呼ばれるモデルを持っています。一意性の妥当性検査が有効でない

Templateモデルは次のように書かれている:

has_many :related_templates, :foreign_key => :parent_id 
has_many :children, :through => :related_templates, :source => :child 

has_many :inverse_related_templates, class_name: "RelatedTemplate", :foreign_key => :child_id 
has_many :parents, :through => :inverse_related_templates, :source => :parent 

validates :name, presence: true, uniqueness: { case_sensitive: false }, 
       length: { maximum: 100, too_long: "should be maximum %{count} characters" } 

参加モデルRelatedTemplateは次のとおりです。コンソールで

belongs_to :parent, class_name: 'Template' 
belongs_to :child, class_name: 'Template' 

私はと同じ名前の子テンプレートを作成しようとしました親の名前の場合、検証は機能しません。

rails c --sandbox 
Loading development environment in sandbox (Rails 4.2.5) 
Any modifications you make will be rolled back on exit 
irb(main):001:0> a = Template.new 
=> #<Template id: nil, client_id: nil, something_id: nil, name: nil, description: nil, another_something_id: nil, video_id: nil, container_id: nil> 
irb(main):002:0> a.something_id=1 
=> 1 
irb(main):003:0> a.name="Hamza" 
=> "Hamza" 
irb(main):004:0> a.another_something_id=16 
=> 16 
irb(main):005:0> a.container_id=2 
=> 2 
irb(main):006:0> a.children.build(name: "Hamza", another_something_id: 16, container_id: 2) 
=> #<Template id: nil, client_id: nil, something_id: nil, name: "Hamza", description: nil, another_something_id: 16, video_id: nil, container_id: 2> 
irb(main):007:0> a.save 
(0.9ms) SAVEPOINT active_record_1 
Template Exists (1.3ms) SELECT 1 AS one FROM "templates" WHERE LOWER("templates"."name") = LOWER('Hamza') LIMIT 1 
Container Load (0.7ms) SELECT "containers".* FROM "containers" WHERE "containers"."id" = $1 LIMIT 1 [["id", 2]] 
Template Exists (0.5ms) SELECT 1 AS one FROM "templates" WHERE LOWER("templates"."name") = LOWER('Hamza') LIMIT 1 
Container Load (0.2ms) SELECT "containers".* FROM "containers" WHERE "containers"."id" = $1 LIMIT 1 [["id", 2]] 
SQL (0.3ms) INSERT INTO "templates" ("something_id", "name", "another_something_id", "container_id") VALUES ($1, $2, $3, $4) RETURNING "id" [["something_id", 1], ["name", "Hamza"], ["another_something_id", 16], ["container_id", 2]] 
SQL (0.2ms) INSERT INTO "templates" ("name", "another_something_id", "container_id") VALUES ($1, $2, $3) RETURNING "id" [["name", "Hamza"], ["another_something_id", 16], ["container_id", 2]] 
SQL (0.6ms) INSERT INTO "related_templates" ("parent_id", "child_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4) RETURNING "id" [["parent_id", 156], ["child_id", 157], ["created_at", "2016-05-05 07:03:51.496346"], ["updated_at", "2016-05-05 07:03:51.496346"]] 
(0.2ms) RELEASE SAVEPOINT active_record_1 
=> true 

今すぐクエリ:

irb(main):016:0> a.name 
=> "Hamza" 
irb(main):017:0> a.children.first.name 
Template Load (1.9ms) SELECT "templates".* FROM "templates" INNER JOIN "related_templates" ON "templates"."id" = "related_templates"."child_id" WHERE "related_templates"."parent_id" = $1 ORDER BY "templates"."id" ASC LIMIT 1 [["parent_id", 158]] 
=> "Hamza" 

ここで何が間違っているのですか?

+0

「has_many:related_templates、:foreign_key =>:parent_id、source =>:template」にソースキーを追加します –

+0

私はそれを実行しましたが、動作しませんでしたか? –

+0

どのように動作しませんか? –

答えて

4

両方のオブジェクトは、検証時にメモリ内にのみ存在します。一意性検証は、データベースにすでに類似のレコードが存在するかどうかを確認するため、両方の場合に合格します。

は、ログを見てください:一意性の検証が100%安全ではありませんRailsの理由

# you call save 
irb(main):007:0> a.save 
(0.9ms) SAVEPOINT active_record_1 

# Rails validates the first object 
Template Exists (1.3ms) SELECT 1 AS one FROM "templates" WHERE LOWER("templates"."name") = LOWER('Hamza') LIMIT 1 
Container Load (0.7ms) SELECT "containers".* FROM "containers" WHERE "containers"."id" = $1 LIMIT 1 [["id", 2]] 

# Rails validates the second object 
Template Exists (0.5ms) SELECT 1 AS one FROM "templates" WHERE LOWER("templates"."name") = LOWER('Hamza') LIMIT 1 
Container Load (0.2ms) SELECT "containers".* FROM "containers" WHERE "containers"."id" = $1 LIMIT 1 [["id", 2]] 

# At this point Rails thinks that both records will be fine, because the validation passed 

# Rails saved the first object 
SQL (0.3ms) INSERT INTO "templates" ("something_id", "name", "another_something_id", "container_id") VALUES ($1, $2, $3, $4) RETURNING "id" [["something_id", 1], ["name", "Hamza"], ["another_something_id", 16], ["container_id", 2]] 

# Rails saved the second object 
SQL (0.2ms) INSERT INTO "templates" ("name", "another_something_id", "container_id") VALUES ($1, $2, $3) RETURNING "id" [["name", "Hamza"], ["another_something_id", 16], ["container_id", 2]] 

これは良い例です。もう1つの例は競合状態で、複数の要求が同時にアプリケーションにヒットする場合があります。

この種の問題を回避する唯一の方法は、データベーステーブルに一意のインデックスを追加することです。

+0

あなたが正しいと思います。 dbテーブルに一意制約を追加します。その後、私はその 'database constraint error'を手動で処理する必要があります。右 ? –

+0

はい、データベースエラーを自分で処理する必要があります。 – spickermann

関連する問題