Skip to content

Save succeeds despite associated record failing to save #32939

@Mangara

Description

@Mangara

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

Executable test

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

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions