Adding reference to a table - ruby-on-rails

Please how can I reference a table when not using the default table id as the table's primary key? I created two tables one having email as the primary key and I want to have this table email as the foreign key in the other table. Please How can i achieve that?

You may create table with email as primary key like this:
create_table :users, id: false do |t|
t.string :email, primary_key: true
t.string :name
t.timestamps
end
create_table :posts do |t|
t.string :title
t.text :body
t.string :user_email
t.timestamps
end
And define your models like this:
class User < ActiveRecord::Base
self.primary_key = 'email'
has_many :posts, foreign_key: :user_email
end
class Post < ActiveRecord::Base
belongs_to :user, foreign_key: :user_email
end
That's all you need to do. Also you may keep id as primary key for User model but use use email as foreign key - this may be easier way to handle your models.

Related

Specifying a different foreign key in a model relationship

I have two models, Staff and Hospital, in a belongs_to has_many relationship. A hospital has many staff. My staff table look like this:
class CreateStaffs < ActiveRecord::Migration[5.1]
def change
create_table :staffs do |t|
t.string :Title
t.string :FirstName
.
.
.
t.integer :hospital_id
t.timestamps
end
end
end
Can't I specify a different foreign key, such as hospital_name?
Thanks
What you need is to create the table like this:
create_table :staffs do |t|
...
t.string :hospital_name
end
add_foreign_key :staffs, :hospitals, column: :hospital_name, primary_key: :name
where with primary_key: :name you reference the column name of the hospitals table.

Rails / postgresQL ActiveRecord - HOW and WHY change the primary key?

I have a Rails project with postgresql database.
Let's say I have three models - Student, Teacher and Schedule - that joins a student and teacher
Student Model - Instead of going with the student_id as my primary key, I want to change that to the even_cooler_unique_student_number that a school has for a student.
Teacher Model - typical & traditional.
Schedule Model - I want to associate a Schedule (think - just a math class for now) with one teacher and its students.
How do I do that at the database level and with the AR associations?
What does changing the primary_key do to the database? To my associations through ActiveRecord?
class CreateStudent < ActiveRecord::Migration
def change
create_table :students do |t|
t.integer :unique_cooler_student_id, null: false
t.string :first_name
t.string :last_name
t.timestamps null: false
end
end
end
class CreateTeacher < ActiveRecord::Migration
def change
create_table :teachers do |t|
t.string :first_name
t.string :last_name
t.string :department
t.timestamps null: false
end
end
end
class CreateSchedules < ActiveRecord::Migration
def change
create_table :schedules, id: false, force: true do |t|
t.belongs_to :students, :primary_key => 'unique_cooler_student_id'
t.belongs_to :teachers
t.string :something_else
t.timestamps null: false
end
end
end
class Student
self.primary_key = 'unique_cooler_student_id'
has_many :teachers, through: :classes
end
class Teacher
has_many :students, through: :classes
end
class Schedule
belongs_to :students
belongs_to :teachers
end
Changing the name of primary key usually does very little besides adding a false sense of security - which is only by obscurity.
You can however change the primary key from a auto-incrementing integer to a hash or some other sort of UUID. And there are many valid reasons to do so. This solely changes the method of generating primary keys.
You can even have separate external UUIDs which are used in url params for example. However this does not involve changing the primary key that ActiveRecord uses to join records:
Foo.joins(:bars).find_by(uuid: 'ABCD')
Of course ActiveRecord will let you crack out the tin-foil hat and use whatever primary keys you want - however you will need to specify the primary_key and probably also manually setup the foreign keys in your database to maintain referential integrity. So basically your losing every advantage of convention over configuration for no benefit.
You would have to do it like this:
class CreateSchedules < ActiveRecord::Migration
def change
create_table :schedules, id: false, force: true do |t|
t.references :students, foreign_key: false
t.belongs_to :teachers
t.string :something_else
t.timestamps null: false
end
end
end
class AddStudensIdContraintToSchedules < ActiveRecord::Migration
def change
add_foreign_key :schedules, :students, primary_key: "unique_cooler_student_id"
end
end
class Schedule
has_many :students, primary_key: 'unique_cooler_student_id'
end
This way AR uses WHERE students.unique_cooler_student_id = 2 in the join query.
The only reason you would ever really want to do this this is if you have to use a legacy database and cannot change the database schema.

How to migrate from using one foreign key to another in ActiveRecord?

Using Postgres as the backing store, I have a table which (at least for the time being) has both an integer primary key and a uuid with a unique index.
It looks something like this in my schema.rb (simplified for example):
create_table "regions", force: cascade do |t|
t.integer "region_id"
t.uuid "uuid", default: "uuid_generate_v4()"
t.string "name"
end
add_index "regions", ["uuid"], name "index_regions_on_uuid", unique: true, using :btree
I then have a table which has a reference to the integer id, something like this:
create_table "sites", force:cascade do
t.integer "site_id"
t.integer "region_id"
t.string "name"
end
What I want to do is to switch from region_id to uuid as the foreign key in the second table. How should I god about writing this migration?
Just create a migration, and inhale some SQL magic into it:
def up
# Create and fill in region_uuid column,
# joining records via still existing region_id column
add_column :sites, :region_uuid
if Site.reflect_on_association(:region).foreign_key == 'region_id'
# We won't use 'joins(:regions)' in case we will need
# to re-run migration later, when we already changed association
# code as suggested below. Specifying join manually instead.
Site.joins("INNER JOIN regions ON site.region_id = regions.id").update_all("region_uuid = regions.uuid")
end
drop_column :sites, :region_id
end
Then you just need to fix your association:
class Site < ActiveRecord::Base
belongs_to :region, primary_key: :uuid, foreign_key: :region_uuid
end
class Region < ActiveRecord::Base
has_many :sites, primary_key: :uuid, foreign_key: :region_uuid
end
From your comment, its seems that you want to modify the primary key referenced by the association, not the foreign key. You actually don't need a migration to do this. Instead, just specify the primary key on the association definitions in each model:
Class Region << ActiveRecord::Base
has_many :sites, primary_key: :uuid
end
Class Site << ActiveRecord::Base
belongs_to :region, primary_key: :uuid
end
The foreign key, since it follows rails convention of being named as the belongs_to relation with an appended "_id" (in this case, region_id), does not need to be specified here.
ETA: You will also need to ensure that the type of sites.region_id matches the type of regions.uuid, which I assume is uuid. I'm also going to assume that this field was previously indexed (under ActiveRecord convention) and that you still want it indexed. You can change all this in a migration like so:
def up
remove_index :sites, :region_id
change_column :sites, :region_id, :uuid
add_index :sites, :region_id
end
def down
remove_index :sites, :region_id
change_column :sites, :region_id, :integer
add_index :sites, :region_id
end

Rails reference a model with a non integer primary key

I have a table whose migration is this:
class CreateClient < ActiveRecord::Migration
def change
create_table :clients, :primary_key => :tag do |t|
t.string :name
end
change_column :clients, :tag, :string, limit: 4
end
end
so the primary key as you can see is that tag field which is a varchar(4), now I need to reference that field from another table.
I tried in the migration to create the reference in this way:
t.references :client, index: true
but the resulting table has a field called client_id which is int, should I pass any parameter to the references method so that it will create the field as varchar?
Thank you in advance
I believe you can use execute to force rails into setting the primary key in the way you want:
class CreateClient < ActiveRecord::Migration
def change
create_table :clients, :id => false do |t|
t.string :name
end
execute "ALTER TABLE clients ADD PRIMARY KEY (name);"
And then set do set_primary_key :name in your Client model

Model associations in RoR

I'm trying to understand how rails works in respect to foreign key and primary keys. Coming from a pure SQL background the Rails method seems very alien to me.
I have the following two migrations:
Groups
class CreateGroups < ActiveRecord::Migration
def self.up
create_table :groups do |t|
t.string :title
t.text :description
t.string :city
t.integer :event_id
t.string :zip
t.string :group_id
t.text :topics
t.timestamps
end
end
def self.down
drop_table :groups
end
end
and Events:
class CreateEvents < ActiveRecord::Migration
def self.up
create_table :events do |t|
t.string :title
t.string :description
t.string :city
t.string :address
t.time :time_t
t.date :date_t
t.string :group_id
t.timestamps
end
end
def self.down
drop_table :events
end
end
A Group can have many events and an event can belong to a single group. I have the following two models:
class Event < ActiveRecord::Base
belongs_to :group, :foreign_key => 'group_id'
end
and
class Group < ActiveRecord::Base
attr_accessible :title, :description, :city, :zip, :group_id, :topics
has_many :events
end
not sure how to specify foreign keys and primary keys to this. For example a group is identified by the :group_id column and using that I need to fetch events that belong to a single group!
how do i do this!
I see you have group_id and event_id as strings in your migration, so I think you might be missing a rails convention. The rails convention is that all tables have a primary key named id of type integer, and any foreign keys reference it by the name of the model, singular, + _id:
table groups:
id: integer
name: string
table events:
id: integer
name: string
group_id: integer
From this convention, all you have to specify in your models is:
class Event < ActiveRecord::Base
belongs_to :group
end
class Group < ActiveRecord::Base
has_many :events
end
At this point, rails knows what to do by convention over configuration: To find an event's group, it knows to look for group_id (singular) to refer to groups.id (plural table name)
event = Event.first #=> returns the first event in database
group = event.group #=> returns the group object
Similarly, it know how to find all the events in a group
group = Group.first #=> returns first group in database
group.events #=> returns an Enumerable of all the events
For more reading, read the rails guide on associations

Resources