Discard composite primary keys in Rails
by mmyoji
2 min read
Most people might not do this, but some try to use composite primary keys in Rails application.
Yes, I met this situation. And they used composite_primary_keys gem.
But this is a bit risky to use this type of gem, it overrides ActiveRecord and no one knows when the maintenance of the gem ends.
I talked about this to the developer team and replaced it with normal id primary key. Though this was not so difficult one, I'll write about the detail in this post.
Steps
- Replace
ActiveRecord::Base#{destroy,delete},.destroy_all
with.delete_all
- Add proper index
- Drop composite primary keys and add
id
column instead
Use .delete_all
As an example, you have ActiveRecord::Base
class PostTag
which has composite
primary keys for post_id
and tag_id
.
class PostTag < ApplicationRecord
# this is a composite_primary_keys method
self.primary_keys = [:post_id, :tag_id]
belongs_to :post # Post class
belongs_to :tag # Tag class
end
The following ActiveRecord::Base
methods try to delete record by id
if you
don't specify .primary_keys=
in the model:
#destroy
#delete
.destroy_all
So, you should replace all of them with .delete_all
.
It must affect nothing with current code because the records are unique by each keys.
# before
post_tag.destroy
# after
PostTag.where(post: post_tag.post, tag: post_tag.tag).delete_all
# or you can just overrides method in PostTag class definition
def destroy
self.class.where(post: post, tag: tag).delete_all
end
Add proper index
If you PostTag table definition is like following:
create_table "post_tags", primary_key: ["post_id", "tag_id"] do |t|
t.bigint "post_id", null: false
t.bigint "tag_id", null: false
t.index ["tag_id"], name: "tag_id"
end
You cannot add id
as primary key, then add indices to all primary keys. In
this example, add index for post_id
.
You also add unique index for a pair of post_id
and tag_id
.
Drop current composite primary keys and add id as primary key
This is the final step. Run the following query for the table.
ALTER TABLE post_tags
DROP PRIMARY KEY,
ADD id BIGINT(20) UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY FIRST;
In this query, I adds:
- type bigint
- unsigned
- not null constraint
- auto_increment
- insert in the first order in the table definition
You can change each of them if you want.