Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Join table cannot update or destroy throw error of no primary key #25347

Closed
starkshaw opened this issue Jun 10, 2016 · 26 comments
Closed

Join table cannot update or destroy throw error of no primary key #25347

starkshaw opened this issue Jun 10, 2016 · 26 comments

Comments

@starkshaw
Copy link

starkshaw commented Jun 10, 2016

Steps to reproduce

Have models as follow:

class Event < ApplicationRecord
  belongs_to :deal
  has_many :events_users
  has_many :users, through: :events_users
end

class Deal < ApplicationRecord
  belongs_to :team
  has_many :deals_users
  has_many :users, through: :deals_users
  has_many :events
end

class Team < ApplicationRecord
  has_many :deals
  has_many :team_users
  has_many :users, through: :team_users
end

class User < ApplicationRecord
  has_one :team, through: :team_user
  has_one :team_user
  has_and_belongs_to_many :deals
  has_and_belongs_to_many :events
end

class EventsUser < ApplicationRecord
  belongs_to :event
  belongs_to :user
  validates_uniqueness_of :event_id, scope: :user_id
  enum status: [:pending, :accepted, :maybe, :rejected]
end

class DealsUser < ApplicationRecord
  belongs_to :user
  belongs_to :deal
  validates_uniqueness_of :deal_id, scope: :user_id
end

class TeamUser < ApplicationRecord
  belongs_to :user
  belongs_to :team
  validates_uniqueness_of :team_id, scope: :user_id
  validates_uniqueness_of :user_id
end

Expected behavior

The join tables, EventsUser, DealsUser, TeamUser can be updated or deleted.

Actual behavior

It can only be created, the update will throw

ActiveRecord::UnknownPrimaryKey: Unknown primary key for table events_users in model EventsUser.
Can not validate uniqueness for persisted record without primary key.

and destroy will throw

NoMethodError: undefined method `to_sym' for nil:NilClass

These following are my testing procedures in rails c.

eu = EventsUser.first
eu.status = "maybe"
eu.save (throw update error)
eu.destroy (throw destroy error)
eu (it exists)
eu.status (return the status)

status is an enum in model, and defined in migration as such:

t.integer :status, default: 0

System configuration

Rails version:
Rails 5.0.0.rc1
Ruby version:
ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]

Reference

#21304

@maclover7
Copy link
Contributor

cc @sgrif

@prakashmurthy
Copy link
Contributor

Tried reproducing this behavior with a demo app; could not reproduce the buggy behavior. The join tables can be updated and deleted as per normal expectation.

The demo app is at https://github.com/prakashmurthy/join_table_issue_demo

Tried with both 5.0.0 and 5.0.0.rc1.

Ruby version: ruby 2.3.1p112 (2016-04-26 revision 54768) [x86_64-darwin15]

Not sure what I am missing....

@ibussieres
Copy link

ibussieres commented Aug 5, 2016

Experiencing the exact same problem, also with a joint table. Rails 5.0.0.

Extract from db/schema.rb

create_table "likes", id: false, force: :cascade do |t|
    t.integer  "item_id"
    t.integer  "owner_id"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["item_id", "owner_id"], name: "index_likes_on_item_id_and_owner_id", unique: true, using: :btree
end

@prakashmurthy's example does not use joint indices. Perhaps that is the root problem ?

@ibussieres
Copy link

fwiw, I changed it to seperated indices and still have the same issue.

@prakashmurthy
Copy link
Contributor

Created a bug_report_template script for this issue; the behavior is not being reproduced by the script. Hopefully, it will help identify the difference that is causing the issue for @starkshaw and @ibussieres .

Script to reproduce the issue with join table update and delete

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"
  # Activate the gem you are reporting the issue against.
  gem "activerecord", "5.0.0"
  gem "sqlite3"
end

require "active_record"
require "minitest/autorun"
require "logger"

# Ensure backward compatibility with Minitest 4
Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test)

# 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 :teams, force: true do |t|
    t.string :name
  end

  create_table :deals, force: true do |t|
    t.string :name
    t.integer :team_id
  end

  create_table :events, force: true do |t|
    t.string :name
    t.integer :deal_id
  end

  create_table :users, force: true do |t|
    t.string :first_name
    t.string :last_name
  end

  create_table :events_users, force: true do |t|
    t.integer :event_id
    t.integer :user_id
    t.integer :status, default: 0
  end

  create_table :deals_users, force: true do |t|
    t.integer :deal_id
    t.integer :user_id
    t.integer :status, default: 0
  end

  create_table :team_users, force: true do |t|
    t.integer :team_id
    t.integer :user_id
    t.integer :status, default: 0
  end
end

class ApplicationRecord < ActiveRecord::Base
  self.abstract_class = true
end

class Event < ApplicationRecord
  belongs_to :deal
  has_many :events_users
  has_many :users, through: :events_users
end

class Deal < ApplicationRecord
  belongs_to :team
  has_many :deals_users
  has_many :users, through: :deals_users
  has_many :events
end

class Team < ApplicationRecord
  has_many :deals
  has_many :team_users
  has_many :users, through: :team_users
end

class User < ApplicationRecord
  has_one :team, through: :team_user
  has_one :team_user
  has_and_belongs_to_many :deals
  has_and_belongs_to_many :events
end

class EventsUser < ApplicationRecord
  belongs_to :event
  belongs_to :user
  validates_uniqueness_of :event_id, scope: :user_id
  enum status: [:pending, :accepted, :maybe, :rejected]
end

class DealsUser < ApplicationRecord
  belongs_to :user
  belongs_to :deal
  validates_uniqueness_of :deal_id, scope: :user_id
end

class TeamUser < ApplicationRecord
  belongs_to :user
  belongs_to :team
  validates_uniqueness_of :team_id, scope: :user_id
  validates_uniqueness_of :user_id
end

User.create(first_name: "Almaz", last_name: "Ayana")
User.create(first_name: "Vivian", last_name: "Cheruiyot")

Team.create(name: "10000m")
Team.create(name: "100m")

Deal.create(name: "Half Off", team_id: 1)
Deal.create(name: "BOGO", team_id: 2)

Event.create(name: "Christmas", deal_id: 1)
Event.create(name: "Halloween", deal_id: 1)

EventsUser.create(user_id: 1, event_id: 1)
EventsUser.create(user_id: 2, event_id: 1)

TeamUser.create(user_id: 1, team_id: 1)
TeamUser.create(user_id: 2, team_id: 2)

DealsUser.create(user_id: 1, deal_id: 1)
DealsUser.create(user_id: 2, deal_id: 2)

class BugTest < Minitest::Test
  def test_join_table_update
    eu = EventsUser.first
    eu.status = "maybe"
    eu.save
    assert_equal "maybe", EventsUser.first.status
    eu.destroy
    assert_equal true, eu.destroyed?
  end
end

@ibussieres
Copy link

It works if I remove id: false. I can work around the problem by testing uniqueness in the model, but it leaves me with a useless id field in a join table.

@sgrif
Copy link
Contributor

sgrif commented Aug 14, 2016

Active Record doesn't have built in support for composite primary keys. You'll need to remove the validates_uniqueness_of. If this isn't an error you expect the user to be able to recover from (e.g. it's just to enforce data integrity), you can use a unique index on the database for the same purpose.

@sgrif sgrif closed this as completed Aug 14, 2016
@sgrif sgrif reopened this Aug 14, 2016
@sgrif
Copy link
Contributor

sgrif commented Aug 14, 2016

Actually if you can reproduce this by deleting through associations, there probably is a real issue here. Please update with a repro script if that's the case.

@lhammond
Copy link

happening for me on 5.0.0.1

@stale
Copy link

stale bot commented Mar 27, 2017

This issue has been automatically marked as stale because it has not been commented on for at least three months.
The resources of the Rails team are limited, and so we are asking for your help.
If you can still reproduce this error on the 5-1-stable branch or on master, please reply with all of the information you have about it in order to keep the issue open.
Thank you for all your contributions.

@stale stale bot added the wontfix label Mar 27, 2017
@roolo
Copy link

roolo commented Apr 1, 2017

Happening to me on Rails 5.0.2

@stale stale bot removed the stale label Apr 1, 2017
@batmanbury
Copy link

Also happening to me on Rails 5.0.2

I have id: false, force: :cascade in the schema and dependent: :destroy in the model. Still happening.

@rohineematale
Copy link

rohineematale commented Apr 3, 2017

Hi @batmanbury ,
Rails is looking for a primary key for deleting a record. Check this
Rails take ID as default primary key and as it's not present it gives a to_sym error for null class.
For temporary basis, you can avoid this issue by mentioning primary_key in model as

self.primary_key = <column_name having uniq index>

or you need to add ID column in a table which will serve as a primary_key.

This solved my issue

@vishalzambre
Copy link
Contributor

@sgrif
This is reproduced by deleting through associations got following exception

BugTest#test_join_table_update:
NoMethodError: undefined method `to_sym' for nil:NilClass

Here is attached template
Verified with versions
activerecord 5.1.0.rc1 and 5.0.0
ruby 2.4.0

It works smooth if we remove id: false from join table

@peracchi
Copy link

peracchi commented May 9, 2017

I think I have the same problem...

rails g model City name:string
rails g model Event title:string city:belongs_to
rails g model Entity name:string
rails g model Sponsorship --no-migration
rails g migration CreateSponsorshipJoinTable event entity
rails db:migrate

Models:

class City < ApplicationRecord
  has_many :events
end

class Event < ApplicationRecord
  belongs_to :city

  has_many :sponsorships, inverse_of: :event
  accepts_nested_attributes_for :sponsorships, reject_if: :all_blank, allow_destroy: true

  has_many :entities, through: :sponsorships
end

class Sponsorship < ApplicationRecord
  belongs_to :event
  belongs_to :entity
end

class Entity < ApplicationRecord
  has_many :sponsorships
  has_many :events, through: :sponsorships
end

Then I try the following on rails console:

params = { title: 'First Event', city_id: 4, sponsorships_attributes: [ { entity_id: 2 }, { entity_id: 5 }, { entity_id: 7 } ] }

event = Event.create(params)

params = { title: 'First Event (first change)', sponsorships_attributes: { "0" => { entity_id: 5, _destroy: 1 } } }

event.update_attributes(params)

params = { title: 'First Event (second change)', sponsorships_attributes: { "0" => { entity_id: 5, _destroy: 1, id: 9 } } }

event.update_attributes(params)

On the first update attempt, nothing happens on sponsorships table.

On the second update attemt I got:
NoMethodError: undefined method to_sym' for nil:NilClass`

Any ideas?

@matthewd
Copy link
Member

matthewd commented May 9, 2017

Per #25347 (comment):

Active Record doesn't have built in support for composite primary keys

That means you can't manipulate a model whose corresponding table doesn't have a single-column primary key defined. That includes doing so through an association that uses said model.

@matthewd matthewd closed this as completed May 9, 2017
@roolo
Copy link

roolo commented Jun 17, 2017

Happens to on Rails 5.1.1

Migration

class CreateUsersSubscriptions < ActiveRecord::Migration[5.1]
  def change
    create_join_table :users, :subscriptions do |t|
      t.references :user, foreign_key: true
      t.references :subscription, foreign_key: true
      t.datetime :since_date
      t.datetime :expire

      t.timestamps
    end
  end
end

rails console:

2.4.1 :010 > frk = SubscriptionsUser.create user: User.first, subscription: Subscription.first
  User Load (1.1ms)  SELECT  "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT $1  [["LIMIT", 1]]
  Subscription Load (0.8ms)  SELECT  "subscriptions".* FROM "subscriptions" ORDER BY "subscriptions"."id" ASC LIMIT $1  [["LIMIT", 1]]
   (0.5ms)  BEGIN
  SQL (0.7ms)  INSERT INTO "subscriptions_users" ("user_id", "subscription_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4)  [["user_id", 1], ["subscription_id", 1], ["created_at", "2017-06-17 20:45:47.117825"], ["updated_at", "2017-06-17 20:45:47.117825"]]
   (23.7ms)  COMMIT
 => #<SubscriptionsUser user_id: 1, subscription_id: 1, since_date: nil, expire: nil, created_at: "2017-06-17 20:45:47", updated_at: "2017-06-17 20:45:47"> 
2.4.1 :011 > frk.destroy
   (1.1ms)  BEGIN
   (1.1ms)  ROLLBACK
NoMethodError: undefined method `to_sym' for nil:NilClass
	from (irb):11

When I try to manually change schema_statements.rb:373 to id: true, I get following

 $ rake db:migrate 
== 20170617204034 CreateUsersSubscriptions: migrating =========================
-- create_join_table(:users, :subscriptions)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

undefined method `to_sym' for true:TrueClass
Did you mean?  to_s
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:313:in `column'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:162:in `primary_key'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/postgresql/schema_definitions.rb:56:in `primary_key'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_statements.rb:278:in `create_table'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_statements.rb:352:in `create_join_table'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:849:in `block in method_missing'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:818:in `block in say_with_time'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:818:in `say_with_time'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:838:in `method_missing'
/Users/mailo/Projects/my/webapps/subscriptions/subscriptions/db/migrate/20170617204034_create_users_subscriptions.rb:3:in `change'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:792:in `exec_migration'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:776:in `block (2 levels) in migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:775:in `block in migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:408:in `with_connection'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:774:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:953:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1230:in `block in execute_migration_in_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1298:in `block in ddl_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:225:in `block in transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/transaction.rb:194:in `block in within_new_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/transaction.rb:191:in `within_new_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:225:in `transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/transactions.rb:210:in `transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1298:in `ddl_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1229:in `execute_migration_in_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1201:in `block in migrate_without_lock'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1200:in `each'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1200:in `migrate_without_lock'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1148:in `block in migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1317:in `with_advisory_lock'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1148:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1007:in `up'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:985:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:171:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/railties/databases.rake:58:in `block (2 levels) in <top (required)>'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/rake-12.0.0/exe/rake:27:in `<top (required)>'
NoMethodError: undefined method `to_sym' for true:TrueClass
Did you mean?  to_s
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:313:in `column'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:162:in `primary_key'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/postgresql/schema_definitions.rb:56:in `primary_key'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_statements.rb:278:in `create_table'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/schema_statements.rb:352:in `create_join_table'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:849:in `block in method_missing'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:818:in `block in say_with_time'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:818:in `say_with_time'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:838:in `method_missing'
/Users/mailo/Projects/my/webapps/subscriptions/subscriptions/db/migrate/20170617204034_create_users_subscriptions.rb:3:in `change'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:792:in `exec_migration'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:776:in `block (2 levels) in migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:775:in `block in migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/connection_pool.rb:408:in `with_connection'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:774:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:953:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1230:in `block in execute_migration_in_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1298:in `block in ddl_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:225:in `block in transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/transaction.rb:194:in `block in within_new_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/transaction.rb:191:in `within_new_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/connection_adapters/abstract/database_statements.rb:225:in `transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/transactions.rb:210:in `transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1298:in `ddl_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1229:in `execute_migration_in_transaction'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1201:in `block in migrate_without_lock'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1200:in `each'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1200:in `migrate_without_lock'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1148:in `block in migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1317:in `with_advisory_lock'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1148:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:1007:in `up'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/migration.rb:985:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/tasks/database_tasks.rb:171:in `migrate'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/activerecord-5.1.1/lib/active_record/railties/databases.rake:58:in `block (2 levels) in <top (required)>'
/Users/mailo/.rvm/gems/ruby-2.4.1@global/gems/rake-12.0.0/exe/rake:27:in `<top (required)>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)

@GomaaK
Copy link

GomaaK commented Nov 19, 2018

I've read the thread and I can't wrap my head around the fact that create_join_table by default creates a join table with no primary_key and at the same time:

Per #25347 (comment):

Active Record doesn't have built in support for composite primary keys

That means you can't manipulate a model whose corresponding table doesn't have a single-column primary key defined. That includes doing so through an association that uses said model.

How can both be true?
Is using create_join_table something we should avoid?
Or am I missing something here?

@Swizec
Copy link

Swizec commented Dec 10, 2018

@GomaaK I'm in the same boat as you. I think if we upgraded to Rails 5 everything would be fine.

@bensaufley
Copy link

I'm in Rails 5 in a new project and encountering this same issue. I echo @GomaaK's confusion. I don't have a model for these join tables; there's nowhere to even stick self.primary_key = if I wanted to. What is create_join_table good for if it can't … be … used?

@andreimoment
Copy link

andreimoment commented Oct 27, 2019

Having this issue on Rails 6.0.

What are best practices here and are there plans to fix the AR behavior with join tables? Ideally join tables would not need a primary id for the dependent: :destroy option to be honored.

I'm switching to manually delete-ing dependencies -- is this the best option?

@kofronpi
Copy link

kofronpi commented Feb 7, 2020

After being tired of these errors I just put a primary key on all my join tables

@rusikf
Copy link

rusikf commented May 20, 2020

The problem is still actual
If we have
models/Page.rb

has_many :items, dependent: destroy
has_many :page_items, through: :items

Page.last.destroy => PG::Error: ERROR: zero-length delimited identifier at or near “”“”

@Meekohi
Copy link
Contributor

Meekohi commented May 4, 2021

Old thread but in case it helps... my understanding is that if you use create_join_table you should also be using has_and_belongs_to_many, not has_many through:. This creates a simple bare join table with no id, and you cannot work with those as objects directly.

@stereobooster
Copy link

It works if you use dependent: :delete_all instead of :destroy

@agiratech-praveenkumar
Copy link

Facing this on Rails 6.1.4.1

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests