rails: Polymorphic has_many :through broken in Rails 3.2.9?
A fresh Rails app with the following models/migrations misbehaves on Rails 3.2.9:
app/models/user.rb:
class User < ActiveRecord::Base
has_many :projects, through: :ownerships
has_many :ownerships, dependent: :destroy
end
app/models/project.rb:
class Project < ActiveRecord::Base
has_many :owners, as: :ownable, through: :ownerships
has_many :ownerships, as: :ownable, dependent: :destroy
end
app/models/ownerships.rb:
class Ownership < ActiveRecord::Base
belongs_to :ownable, polymorphic: true
belongs_to :owner, :class_name => 'User', foreign_key: 'user_id'
end
Migration:
class CreateAll < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
end
create_table :projects do |t|
t.string :name
end
create_table :ownerships do |t|
t.references :user, null: false
t.references :ownable, polymorphic: true, null: false
end
end
end
On Rails 3.2.8:
Loading development environment (Rails 3.2.8)
>> u = User.new {|u| u.name = 'Testuser' }
=> #<User id: nil, name: "Testuser">
>> u.save
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "users" ("name") VALUES (?) [["name", "Testuser"]]
(2.8ms) commit transaction
=> true
>> p = Project.new {|p| p.name = 'Testproject' }
=> #<Project id: nil, name: "Testproject">
>> p.owners << u
=> [#<User id: 7, name: "Testuser">]
>> p.ownerships
=> []
>> p.save
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "projects" ("name") VALUES (?) [["name", "Testproject"]]
SQL (2.4ms) INSERT INTO "ownerships" ("ownable_id", "ownable_type", "user_id") VALUES (?, ?, ?) [["ownable_id", 4], ["ownable_type", "Project"], ["user_id", 7]]
(2.6ms) commit transaction
=> true
On Rails 3.2.9:
Loading development environment (Rails 3.2.9)
>> u = User.new {|u| u.name = 'Testuser' }
=> #<User id: nil, name: "Testuser">
>> u.save
(0.1ms) begin transaction
SQL (0.5ms) INSERT INTO "users" ("name") VALUES (?) [["name", "Testuser"]]
(2.1ms) commit transaction
=> true
>> p = Project.new {|p| p.name = 'Testproject' }
=> #<Project id: nil, name: "Testproject">
>> p.owners << u
=> [#<User id: 8, name: "Testuser">]
>> p.ownerships
=> [#<Ownership id: nil, user_id: 8, ownable_id: nil, ownable_type: "Project">]
>> p.save
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "projects" ("name") VALUES (?) [["name", "Testproject"]]
SQL (3.6ms) INSERT INTO "ownerships" ("ownable_id", "ownable_type", "user_id") VALUES (?, ?, ?) [["ownable_id", nil], ["ownable_type", "Project"], ["user_id", 8]]
SQLite3::ConstraintException: ownerships.ownable_id may not be NULL: INSERT INTO "ownerships" ("ownable_id", "ownable_type", "user_id") VALUES (?, ?, ?)
(2.3ms) rollback transaction
ActiveRecord::StatementInvalid: SQLite3::ConstraintException: ownerships.ownable_id may not be NULL: INSERT INTO "ownerships" ("ownable_id", "ownable_type", "user_id") VALUES (?, ?, ?)
from /.../ruby-1.9.3-p327@rails-playground/gems/sqlite3-1.3.6/lib/sqlite3/statement.rb:108:in `step'
from /.../ruby-1.9.3-p327@rails-playground/gems/sqlite3-1.3.6/lib/sqlite3/statement.rb:108:in `block in each'
from /.../ruby-1.9.3-p327@rails-playground/gems/sqlite3-1.3.6/lib/sqlite3/statement.rb:107:in `loop'
from /.../ruby-1.9.3-p327@rails-playground/gems/sqlite3-1.3.6/lib/sqlite3/statement.rb:107:in `each'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/sqlite_adapter.rb:263:in `to_a'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/sqlite_adapter.rb:263:in `block in exec_query'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/abstract_adapter.rb:280:in `block in log'
from /.../ruby-1.9.3-p327@rails-playground/gems/activesupport-3.2.9/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/abstract_adapter.rb:275:in `log'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/sqlite_adapter.rb:242:in `exec_query'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/abstract/database_statements.rb:63:in `exec_insert'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/abstract/database_statements.rb:90:in `insert'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/abstract/query_cache.rb:14:in `insert'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/relation.rb:66:in `insert'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/persistence.rb:367:in `create'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/timestamp.rb:58:in `create'
... 43 levels...
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/validations.rb:50:in `save'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/attribute_methods/dirty.rb:22:in `save'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/transactions.rb:259:in `block (2 levels) in save'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/transactions.rb:313:in `block in with_transaction_returning_status'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/connection_adapters/abstract/database_statements.rb:192:in `transaction'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/transactions.rb:208:in `transaction'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/transactions.rb:311:in `with_transaction_returning_status'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/transactions.rb:259:in `block in save'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/transactions.rb:270:in `rollback_active_record_state!'
from /.../ruby-1.9.3-p327@rails-playground/gems/activerecord-3.2.9/lib/active_record/transactions.rb:258:in `save'
from (irb):8
from /.../ruby-1.9.3-p327@rails-playground/gems/railties-3.2.9/lib/rails/commands/console.rb:47:in `start'
from /.../ruby-1.9.3-p327@rails-playground/gems/railties-3.2.9/lib/rails/commands/console.rb:8:in `start'
from /.../ruby-1.9.3-p327@rails-playground/gems/railties-3.2.9/lib/rails/commands.rb:41:in `<top (required)>'
from script/rails:6:in `require'
from script/rails:6:in `<main>'>>
About this issue
- Original URL
- State: closed
- Created 12 years ago
- Comments: 51 (29 by maintainers)
@aaronbrethorst I’ve been going through a lot of this as well. It seems a lot of the problems come from join models that have presence validations. You can specify
inverse_of
on your “joining” models to keep validations happy.Warning: Its late, and I’m sleepy. Please don’t hold the above against me 😃