Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A has_many Bs where B has no primary key

I've got models A and B; A has_many B, and B belongs_to A. So far, so good—except, I specify that B doesn't have a primary key. I don't plan to ever modify or delete individual B rows, and I expect to have several million to billion of them, so omitting the primary key will be really handy, space-wise.

The migration to create B's table looked like this:

class CreateBs < ActiveRecord::Migration
  def change
    create_table :bs, {:id => false} do |t|
      # … rest of fields …
    end
  end
end

Unfortunately, ActiveRecord disagrees; trying to create an A (that's right!) results in:

1.9.3p194 :001 > A.create!
   (0.3ms)  BEGIN
   (0.1ms)  ROLLBACK
ActiveRecord::UnknownPrimaryKey: ActiveRecord::UnknownPrimaryKey
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/reflection.rb:366:in `primary_key'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/reflection.rb:216:in `association_primary_key'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/has_many_association.rb:104:in `foreign_key_present?'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/association.rb:165:in `find_target?'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/collection_association.rb:332:in `load_target'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/collection_proxy.rb:44:in `load_target'
…

If you catch the exception, its message states:

"Unknown primary key for table bs in model B." 

(which would be because B has no primary key.)

I'd like to not have this issue! Is there any way?

like image 295
Asherah Avatar asked Oct 06 '22 21:10

Asherah


2 Answers

The culprit turned out to be a detail missing from the question—of course—and missing from my working memory:

class A < ActiveRecord::Base
  has_many :bs

  validates :bs, :presence => true
end

Though I thought nothing about it at the time, there was—among many other validations—a validation of the presence of bs. If you squint really hard at the full backtrace—which I was "helpful" enough to truncate in my original question, you see:

ActiveRecord::UnknownPrimaryKey: ActiveRecord::UnknownPrimaryKey
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/reflection.rb:366:in `primary_key'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/reflection.rb:216:in `association_primary_key'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/has_many_association.rb:104:in `foreign_key_present?'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/association.rb:165:in `find_target?'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/collection_association.rb:332:in `load_target'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/collection_proxy.rb:44:in `load_target'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activerecord-3.2.8/lib/active_record/associations/collection_proxy.rb:87:in `method_missing'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.8/lib/active_model/errors.rb:255:in `block in add_on_blank'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.8/lib/active_model/errors.rb:253:in `each'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.8/lib/active_model/errors.rb:253:in `add_on_blank'
        from /Users/annelicuss/.rvm/gems/ruby-1.9.3-p194/gems/activemodel-3.2.8/lib/active_model/validations/presence.rb:8:in `validate'
-------------------------------------------------------------------------------------------------^^^^^^^^^^^^^^^^^^^^^^^

The error occurs in active_model/validations/presence.rb! Removing this constraint is enough to get us going. It can't find the target of the validation, as it tries to use the primary key to do so, and hence fails bad.

like image 148
Asherah Avatar answered Oct 10 '22 01:10

Asherah


Hmm, that is very odd. For some reason, I cannot replicate the problem on my end given the information you posted. I created a new project, created an A model followed by a B model that references A, and ran the migrations. My B table does not have a primary key, and only a foreign key to A, as expected.

The only thing I can suggest is the following

Write your A model like this:

class A < ActiveRecord::Base
  has_many :b, :primary_key=>:myPrimaryKeyFunction # name this function whatever you want, other than :id of course
end

Then, in the B model, simply create the corresponding "myPrimaryKeyFunction" function.

Other than that, I'm not sure what else to suggest. What version of rails and which DB are you running?

like image 21
Paul Richter Avatar answered Oct 10 '22 03:10

Paul Richter