简体   繁体   English

唯一性验证不起作用

[英]Validation for uniqueness not working

I have a model called as Template which has self referential has-many through association with a join model called as RelatedTemplate . 我有一个名为Template的模型,该模型self referential has-many through与一个称为RelatedTemplate联接模型相关联而具有self referential has-many through

The Template model is written like this: 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" }

The join-model RelatedTemplate is: RelatedTemplate模型RelatedTemplate是:

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

In the console when i tried to create a child template with the same name as the parent name then the validation doesn't work: 在控制台中,当我尝试创建与父名称同名的子模板时,验证不起作用:

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

Now the query: 现在查询:

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"

Dont know what is wrong here ? 不知道这是怎么回事?

Both object only exist in memory, when you validate them. 验证它们时,这两个对象仅存在于内存中。 The uniqueness validation passes in both cases, because the validation checks if there is a similar record in the database already. 在这两种情况下,唯一性验证都会通过,因为验证会检查数据库中是否已经存在类似的记录。

Have a look at the logs: 看一下日志:

# 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]]

This is good example why Rails uniqueness validations are not 100% secure. 这是一个很好的例子,说明Rails唯一性验证不是100%安全的。 Another example might be race conditions, when multiple requests hit the application at the same time. 另一个示例可能是竞争条件,当多个请求同时命中该应用程序时。

The only way to avoid this kind of problems, is to add a unique index to the database table. 避免此类问题的唯一方法是向数据库表添加唯一索引。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM