3

I'm using Rails 5.0 with Postgresql 9.5

I'm in need to add composite primary keys to my model 'User_achievement' (to link 'User' and 'Achievement' models as you may guess).

So I tried using the "composite_primary_keys" gem. I followed all the instructions, nevertheless, the result wasn't like I expected. Seems like it doesn't create pkey in the 'user_achievement' table according to the info by psql tool:

test1_development=> \d user_achievements

Table "public.user_achievements"
Column | Type | Modifiers 

----------------+---------+-----------

user_id | integer | 
achievement_id | integer | 
uach_date | date | 

Indexes:

"index_user_achievements_on_achievement_id" btree (achievement_id)

"index_user_achievements_on_user_id" btree (user_id)

Foreign-key constraints:

"fk_rails_4efde02858" FOREIGN KEY (user_id) REFERENCES users(id)

"fk_rails_c44f5b3b25" FOREIGN KEY (achievement_id) REFERENCES achievements(id)

Here's models and migrations' code:

class CreateUsers < ActiveRecord::Migration[5.0]
 def change
  create_table :users do |t|
  t.string :name
  end
 end
end

class CreateAchievements < ActiveRecord::Migration[5.0]
 def change
  create_table :achievements do |t|
  t.string :ach_name
  t.text :ach_desc
  end
 end
end

class CreateUserAchievements < ActiveRecord::Migration[5.0]
  def change
   create_table :user_achievements, id: false do |t|
   t.belongs_to :user, :foreign_key => [:id]
   t.belongs_to :achievement, :foreign_key => [:id]
   t.date :uach_date
   end
  end
 end


class Achievement < ApplicationRecord
  has_many :user_achievements
end

class User < ApplicationRecord
  has_many :user_achievements
end

class UserAchievement < ApplicationRecord
  self.primary_keys = :user_id, :achievement_id
  belongs_to :user, :foreign_key => [:id]
  belongs_to :achievement, :foreign_key => [:id]
end

So should the gem alter db tables? or it influences just the rails' environment? Is there the only way to alter db - to add execute line in migration?

Alexander Gorg
  • 1,049
  • 16
  • 32

1 Answers1

3

Since nobody have posted anything useful, I'll share what I've got by now.

Gem 'composite_primary_key' (CPK) doesn't change anything in database, so if you still want it - you should do it manually (add the command to migration).

The only thing CPK does is extending Rails with 'understanding' what a composite primary key is, since it originally doesn't have and shows an error.

Anyway I found it is very sohpisticated and problematic to use composite primary keys in Rails apps because it makes using other gems quite nerveous (with every new gem installation you keep in mind that somewhere something can go wrong) as well as changing your code with cpks in future. Also it makes your code harder to understand for other people who are not familliar with this feature. So you should always specify that you have used CPK.

A very good alternative is adding an index with 'unique' option which technically means quite the same but doesn't require additional headache.

So use it only if you really have to and there's no other less sophisticated solution!

Alexander Gorg
  • 1,049
  • 16
  • 32
  • 1
    The documentation for the `composite_primary_keys` gem is rather sparse. I would instead suggest using Sequel, as suggested by Overbryd in https://stackoverflow.com/a/42185901/3017719. – Patrick Mar 01 '19 at 11:12
  • @Patrick this seems to be only a solution for postresql, is there any for mysql as well which actually adds the transient record to database? – hiradyazdan Jul 29 '19 at 15:41
  • 1
    @hiradyazdan According to the GitHub [README](https://github.com/jeremyevans/sequel#sequel-the-database-toolkit-for-ruby), Sequel has an adapter for MySQL too. – Patrick Aug 08 '19 at 15:30