-
Notifications
You must be signed in to change notification settings - Fork 351
Home
Ruby on Rails does not support composite primary keys. This free software is an extension
to the database layer of Rails – ActiveRecord – to support composite primary keys as transparently as possible.
Any Ruby script using ActiveRecord can use Composite Primary Keys with this library.
sudo gem install composite_primary_keys
Rails: Add the following to the bottom of your environment.rb
file
require 'composite_primary_keys'
Ruby scripts: Add the following to the top of your script
require 'rubygems' require 'composite_primary_keys'
A model with composite primary keys would look like…
class Membership < ActiveRecord::Base # set_primary_keys *keys - turns on composite key functionality set_primary_keys :user_id, :group_id belongs_to :user belongs_to :group has_many :statuses, :class_name => 'MembershipStatus', :foreign_key => [:user_id, :group_id] end
A model associated with a composite key model would be defined like…
class MembershipStatus < ActiveRecord::Base belongs_to :membership, :foreign_key => [:user_id, :group_id] end
That is, associations can include composite keys too. Nice.
Once you’ve created your models to specify composite primary keys (such as the Membership class) and associations (such as MembershipStatus#membership), you can uses them like any normal model with associations.
But first, lets check out our primary keys.
MembershipStatus.primary_key # => "id" # normal single key Membership.primary_key # => [:user_id, :group_id] # composite keys Membership.primary_key.to_s # => "user_id,group_id"
Now we want to be able to find instances using the same syntax we always use for ActiveRecord…
MembershipStatus.find(1) # single id returns single instance => <MembershipStatus:0x392a8c8 @attributes={"id"=>"1", "status"=>"Active"}> Membership.find(1,1) # composite ids returns single instance => <Membership:0x39218b0 @attributes={"user_id"=>"1", "group_id"=>"1"}>
Using Ruby on Rails? You’ll want to your url_for helpers
to convert composite keys into strings and back again…
Membership.find(:first).to_param # => "1,1"
And then use the string id within your controller to find the object again
params[:id] # => '1,1' Membership.find(params[:id]) => <Membership:0x3904288 @attributes={"user_id"=>"1", "group_id"=>"1"}>
That is, an ActiveRecord supporting composite keys behaves transparently
throughout your application. Just like a normal ActiveRecord.
Membership.find [1,1], [2,1] => [ <Membership:0x394ade8 @attributes={"user_id"=>"1", "group_id"=>"1"}>, <Membership:0x394ada0 @attributes={"user_id"=>"2", "group_id"=>"1"}> ]
Perform #count
operations
MembershipStatus.find(:first).memberships.count # => 1
From Pete Sumskas:
I ran into one problem that I didn’t see mentioned on this list –
and I didn’t see any information about what I should do to address it in the
documentation (might have missed it).The problem was that the urls being generated for a ‘show’ action (for
example) had a syntax like:/controller/show/123000,Bu70for a two-field composite PK. The default routing would not match that,
so after working out how to do the routing I added:map.connect ‘:controller/:action/:id’, :id => /\w+(,\w+)*/to my
route.rb
file.
A suite of unit tests have been run on the following databases supported by ActiveRecord:
Database | Test Success | User feedback |
---|---|---|
mysql | YES | YES (Yes! or No…) |
sqlite3 | YES | YES (Yes! or No…) |
postgresql | YES | YES (Yes! or No…) |
oracle | YES | YES (Yes! or No…) |
sqlserver | ??? (I can help) | ??? (Yes! or No…) |
db2 | ??? (I can help) | ??? (Yes! or No…) |
firebird | ??? (I can help) | ??? (Yes! or No…) |
sybase | ??? (I can help) | ??? (Yes! or No…) |
openbase | ??? (I can help) | ??? (Yes! or No…) |
frontbase | ??? (I can help) | ??? (Yes! or No…) |
http://www.drnicwilliams.com – for future announcements and
other stories and things.
http://groups.google.com/group/compositekeys
- Fork the project.
- Make your feature addition or bug fix.
- Add tests for it. This is important so I don’t break it in a
future version unintentionally. - Commit, do not mess with rakefile, version, or history.
(if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull) - Send me a pull request. Bonus points for topic branches.
This code is free to use under the terms of the MIT licence.
Comments are welcome. Send an email to Dr Nic Williams.