-
Notifications
You must be signed in to change notification settings - Fork 21.9k
Closed
Description
Steps to reproduce
Models:
class Parent < ApplicationRecord
has_many :children
end
class Child < ApplicationRecord
belongs_to :parent
validates :name, uniqueness: true
end
Code:
parent = Parent.new(children: [Child.new(name: 'Wiske'), Child.new(name: 'Wiske')])
parent.save
Expected behavior
The children are invalid, so save
should return false
and no changes should be made to the database.
Actual behavior
It returns true
after saving the parent and one of the children:
parent = Parent.new(children: [Child.new(name: 'Wiske'), Child.new(name: 'Wiske')])
parent.valid? # true
# Child Exists (0.6ms) SELECT 1 AS one FROM `children` WHERE `children`.`name` = BINARY 'Wiske' LIMIT 1
# Child Exists (0.5ms) SELECT 1 AS one FROM `children` WHERE `children`.`name` = BINARY 'Wiske' LIMIT 1
parent.save # true
# (0.6ms) BEGIN
# Child Exists (0.6ms) SELECT 1 AS one FROM `children` WHERE `children`.`name` = BINARY 'Wiske' LIMIT 1
# Child Exists (0.5ms) SELECT 1 AS one FROM `children` WHERE `children`.`name` = BINARY 'Wiske' LIMIT 1
# Parent Create (0.5ms) INSERT INTO `parents` (`created_at`, `updated_at`) VALUES ('2018-05-20 19:14:06', '2018-05-20 19:14:06')
# Child Exists (0.6ms) SELECT 1 AS one FROM `children` WHERE `children`.`name` = BINARY 'Wiske' LIMIT 1
# Child Create (7.2ms) INSERT INTO `children` (`name`, `parent_id`) VALUES ('Wiske', 5)
# Child Exists (0.6ms) SELECT 1 AS one FROM `children` WHERE `children`.`name` = BINARY 'Wiske' LIMIT 1
# (2.8ms) COMMIT
parent.persisted? # true
parent.children # [#<Child id: 6, name: "Wiske", parent_id: 5>, #<Child id: nil, name: "Wiske", parent_id: 5>]
parent.children[1].errors.full_messages # ["Name has already been taken"]
persisted_parent = Parent.find(parent.id)
persisted_parent.children # [#<Child id: 6, name: "Wiske", parent_id: 5>]
Since there are no uniqueness problems between each child individually and what's already in the database, validation of the parent passes. Each child is revalidated before being saved, so when the second child is saved, the duplication is detected and it is not inserted into the database. For some reason, this does not roll back the transaction.
System configuration
Rails version: 5.2.0
Ruby version: 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
(See also #32940)
Metadata
Metadata
Assignees
Labels
No labels