begin
require "bundler/inline"
rescue LoadError => e
$stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
raise e
end
gemfile(true) do
source "https://rubygems.org"
gem "rails", github: "rails/rails"
gem "sqlite3"
end
require "active_record"
require "minitest/autorun"
require "logger"
# This connection will do for database-independent bug reports.
ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
ActiveRecord::Base.logger = Logger.new(STDOUT)
ActiveRecord::Schema.define do
create_table :posts, force: true
create_table :comments, force: true
create_table :post_comments do |t|
t.integer :post_id, null: false
t.integer :comment_id, null: false
end
end
class Post < ActiveRecord::Base
has_many :comments, through: :post_comments
has_many :post_comments
end
class Comment < ActiveRecord::Base
has_many :posts, through: :post_comments
has_many :post_comments
end
class PostComment < ActiveRecord::Base
belongs_to :post
belongs_to :comment
end
class BugTest < Minitest::Test
def test_association_stuff
assert_instance_of Post, Post.create(comments: [Comment.create])
end
end
That the post_comments
insert has the post_id
and does not violate the non null constraint. There are a couple of related issues 17015 and 16494 but this fails without using accepts_nested_attributes_for
which the other two issues seem related to, if this is a bug I'd be happy to investigate, help out, and/or have a go at submitting a PR.
Transaction begins, comment is inserted, transaction is committed. Transaction begins, post is inserted, post comment is inserted but without the post_id
.
BugTest#test_association_stuff:
ActiveRecord::StatementInvalid: SQLite3::ConstraintException: NOT NULL constraint failed: post_comments.post_id: INSERT INTO "post_comments" ("comment_id") VALUES (?)
D, [2016-10-19T22:44:28.638640 #19520] DEBUG -- : (0.0ms) begin transaction
D, [2016-10-19T22:44:28.639217 #19520] DEBUG -- : SQL (0.0ms) INSERT INTO "comments" DEFAULT VALUES
D, [2016-10-19T22:44:28.639376 #19520] DEBUG -- : (0.0ms) commit transaction
D, [2016-10-19T22:44:28.662915 #19520] DEBUG -- : (0.0ms) begin transaction
D, [2016-10-19T22:44:28.663491 #19520] DEBUG -- : SQL (0.0ms) INSERT INTO "posts" DEFAULT VALUES
D, [2016-10-19T22:44:28.665346 #19520] DEBUG -- : SQL (0.2ms) INSERT INTO "post_comments" ("comment_id") VALUES (?) [["comment_id", 1]]
D, [2016-10-19T22:44:28.665540 #19520] DEBUG -- : (0.0ms) rollback transaction
Rails version: 5.0.0.1 & 4.2.7.1
Ruby version: 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]
I've spent some time looking into this and the problem is the order of the has_many
methods since in this example the through
associations are defined before the associations that they go through. This issue only exists when the methods are called in the wrong order and a null constraint exists on the join table. However I think it could be useful to check for this when the associations are defined.
An option would be adding a check to ensure that the through
association has already been defined in the case of having null constraints on the table and printing a warning. Or alternatively we could raise an ArgumentError
(with deprecation warning first) as is done in other cases eg. when defining associations that conflict with AR methods. I'd be happy to work on this, let me know if anyone has feedback.
@cih I believe that's the route to go聽(deprecation warning, for now). It's obviously an "edge case" since it hasn't been tackled before, but it's a definite change in behavior from 3.x to 4+. Seems there are quite a few people hitting it based on the dupes. Perhaps a note in the documentation as well?
Most helpful comment
I've spent some time looking into this and the problem is the order of the
has_many
methods since in this example thethrough
associations are defined before the associations that they go through. This issue only exists when the methods are called in the wrong order and a null constraint exists on the join table. However I think it could be useful to check for this when the associations are defined.An option would be adding a check to ensure that the
through
association has already been defined in the case of having null constraints on the table and printing a warning. Or alternatively we could raise anArgumentError
(with deprecation warning first) as is done in other cases eg. when defining associations that conflict with AR methods. I'd be happy to work on this, let me know if anyone has feedback.