Change reference to different model rails migration - ruby-on-rails

How can I migrate the Referral table to point to point to the PatientProfile id instead of the User id for an existing database (i.e. would like to migrate all existing rows)?
class User
belongs_to :profile, polymorphic: true
has_many :referrals
class PatientProfile
has_one :user, as: :profile
class Referral
belongs_to :user
I could accomplish this by simply making a migration and renaming the user_id column to be patient_profile_id and changing the reference in the model, however that would not migrate over existing records.

I wouldn't simply change the column name. I'd do something like this
class AddPatientProfileToReferrals < ActiveRecord::Migration
def up
add_column :referrals, :patient_profile_id
add_index :referrals, :patient_profile_id
Referral.reset_column_information
Referral.includes(:user).find_each do |referral|
referral.update_attribute(:patient_profile_id, referral.user.profile_id)
end
remove_column :referrals, :user_id
end
end
You probably want to make sure the referrals have a user, and you can simply reverse the process to write the down method.

Related

Rails Composite Keys - Setting up models and assocs

I'm using Rails Composite Keys for the first time and even though I've read the docs, I'm still unsure how to set up the models correctly.
An Activity can be Scheduled by a Provider. I want a join table that tracks activity_id and provider_id as a unique composite key so that I can associate it with something else (prices). The composite needs to be generated upon the creation of a new schedule. The provider does not necessarily own the activity themselves directly.
I've got this for my composite key as it's own model. Is this right?
class ScheduledActivity < ActiveRecord::Base
self.primary_keys = :provider_id, :activity_id
belongs_to :activity
belongs_to :provider
has_many :schedules, :foreign_key => [:provider_id, :activity_id]
has_many :prices, :foreign_key => [:provider_id, :activity_id]
end
This for the db migration:
class CreateJoinTableScheduledActivities < ActiveRecord::Migration
def change
create_join_table :providers, :activities, table_name: :scheduled_activities do |t|
t.index [:provider_id, :activity_id]
end
end
end
How do I then get new entries on the join table as Schedules are created? Do I put only belongs_to :scheduled_activity on this model - i.e. no provider and activity ids on the Schedule table, and do I write a separate hook in order to create the new composite keys?
(Also - is this the correct use case for composite keys in the first place?!)
Thanks in advance!
Well I'm not sure if this fully answers your question but if you have a composite unique key you can do something like the following.
In your migration:
add_index :scheduled_activities, [:provider_id, :activity_id], unique: true
In your join model:
belongs_to :activity
belongs_to :provider
validates :activity, presence: true, uniqueness: { scope: :provider }
Given that this is more than a simple join table maybe you should consider using id's to identify each ScheduledActivity, then you can simply add that reference to your Schedule model/table (Schedule belongs_to :schedule_activity - schedules table will have a reference to schedule_activity with attribute schedule_activity_id, instead of using a composite key). It would make things much simpler and efficient.

Missing Attribute Error

I'm working with Rails, and when setting up some tests I encountered:
ActiveModel::MissingAttributeError:
can't write unknown attribute `group_id`
I'm guessing the issue is in my relations. I have
class Group < ActiveRecord::Base
has_many :transactions
has_many :users
end
And
class Transaction < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
And lastly,
class User < ActiveRecord::Base
belongs_to :group
has_many :transactions
end
I saw that someone had the same error because they were using has_one rather than belongs_to and needed to add an ID column to their DB. I'm using belongs_to though, so I don't think that's what I need? Any ideas?
Looks like you don't have the group_id column in your db.
You must remember that Rails is built on top of a relational database, which means that you can access "related" data by referencing a foreign_key.
When setting up a belongs_to / has_many association, the belongs_to table needs to have the appropriate foreign key (in your case group_id):
Your error doesn't state which model you're receiving the exception for; I would hazard a guess that it's User or Transaction.
--
To fix it, I would recommend creating a migration to add the group_id attribute to the appropriate model:
$ rails g migration AddGroupId
#db/migrate/add_group_id____________.rb
class AddGroupID < ActiveRecord::Migration
def change
add_column :users, :group_id, :integer
end
end
$ rake db:migrate
Unless you created the model you are referring to with a migration that had references, you will still need a migration in your database. An easy way to check if the database has one is to visit your some_project_root/db/schema.rb. If you don't see a the field you want there then you will have to generate one. The way you would do so is to run a rails g migration AddXidToY x_id:integer . It should set a field up for the id in the table you want.

Rails: Address model being used twice, should it be separated into two tables?

I am making an ecommerce site, and I have Purchases which has_one :shipping_address and has_one :billing_address
In the past the way I've implemented this is to structure my models like so:
class Address < ActiveRecord::Base
belongs_to :billed_purchase, class_name: Purchase, foreign_key: "billed_purchase_id"
belongs_to :shipped_purchase, class_name: Purchase, foreign_key: "shipped_purchase_id"
belongs_to :state
end
class Purchase < ActiveRecord::Base
INCOMPLETE = 'Incomplete'
belongs_to :user
has_one :shipping_address, class: Address, foreign_key: "shipped_purchase_id"
has_one :billing_address, class: Address, foreign_key: "billed_purchase_id"
...
end
As you can see, I reuse the Address model and just mask it as something else by using different foreign keys.
This works completely find, but is there a cleaner way to do this? Should I be using concerns? I'm sure the behavior of these two models will always be 100% the same, so I'm not sure if splitting them up into two tables is the way to go. Thanks for your tips.
EDIT The original version of this was wrong. I have corrected it and added a note to the bottom.
You probably shouldn't split it into two models unless you have some other compelling reason to do so. One thing you might consider, though, is making the Address model polymorphic. Like this:
First: Remove the specific foreign keys from addresses and add polymorphic type and id columns in a migration:
remove_column :addresses, :shipping_purchase_id
remove_column :addresses, :billing_purchase_id
add_column :addresses, :addressable_type, :string
add_column :addresses, :addressable_id, :integer
add_column :addresses, :address_type, :string
add_index :addresses, [:addressable_type, :addressable_id]
add_index :addresses, :address_type
Second: Remove the associations from the Address model and add a polymorphic association instead:
class Address < ActiveRecord::Base
belongs_to :addressable, polymorphic: true
...
end
Third: Define associations to it from the Purchase model:
class Purchase < ActiveRecord::Base
has_one :billing_address, -> { where(address_type: "billing") }, as: :addressable, class_name: "Address"
has_one :shipping_address, -> { where(address_type: "shipping") }, as: :addressable, class_name: "Address"
end
Now you can work with them like this:
p = Purchase.new
p.build_billing_address(city: "Phoenix", state: "AZ")
p.build_shipping_address(city: "Indianapolis", state: "IN")
p.save!
...
p = Purchase.where(...)
p.billing_address
p.shipping_address
In your controllers and views this will work just like what you have now except that you access the Purchase for an Address by calling address.addressable instead of address.billed_purchase or address.shipped_purchase.
You can now add additional address joins to Purchase or to any other model just by defining the association with the :as option, so it is very flexible without model changes.
There are some disadvantages to polymorphic associations. Most importantly, you can't eager fetch from the Address side in the above setup:
Address.where(...).includes(:addressable) # <= This will fail with an error
But you can still do it from the Purchase side, which is almost certainly where you'd need it anyway.
You can read up on polymorphic associations here: Active Record Association Guide.
EDIT NOTE: In the original version of this, I neglected to add the address_type discriminator column. This is pernicious because it would seem like it is working, but you'd get the wrong address records back after the fact. When you use polymorphic associations, and you want to associate the model to another model in more than one way, you need a third "discriminator" column to keep track of which one is which. Sorry for the mixup!
In addtion to #gwcoffey 's answer.
Another option would be using Single Table Inhertinace which perhaps suits more for that case, because every address has a mostly similar format.

Rails 3 Migration update_all with relation / join

Given Following Models
class User
has_many :conversations
end
class Conversation
belongs_to :user
has_many :messages
end
class Message
belongs_to :conversation
end
I want to remove the Conversation model and migrate the reference to a user to Message.
Normally I would use something like
add_column :messages, :user_id, :integer
Message.reset_column_information
Message.all.each do |message|
message.user_id = message.conversation.user_id
end
remove_column :messages, :conversation_id
But in Production migrations run after the code was updated. Therefore this would throw an error.
Probably I just need a little hint.
Message should still have 'conversation_id' as a field even though you removed the belongs: to relationship right?
So what if you did:
message.user_id = User.find_by_id(message.conversation_id).user_id
A good solution is to define a temporary model inside the migration -> Source: Rails Guides

Rails updating a many to many record

I'm getting stuck at trying to update an existing many to many record.
Project model:
class Project < ActiveRecord::Base
belongs_to :assignment
belongs_to :programmer
end
Programmer model:
class Programmer < ActiveRecord::Base
has_many :projects
has_many :assignments, :through => :projects
end
Assignment model:
class Assignment < ActiveRecord::Base
has_many :projects
has_many :programmers, :through => :projects
end
so I have data linked up like so:
p = Programmer.create(:name => "Mike")
p.assignments.create(:name => "homework4")
p.assignments[0] = Assignment.find_or_create_by_name("homework1")
p.save
so as you can see, I'm trying to update the association of Mike's first hw to "homework1". All the homework assignments are already in the assignments table so it shoud just find "homework1" and assign it to mike. unfortunately, when I type the third line there are no errors, but it doesn't update it. In memory, p.assignments == homework1, but in the DB it's still the same(even after p.save). The project's join table isn't changed at all.
the logs of mysql show this command being generated whenever I enter the 3rd line.
SELECT "assignments".* FROM "assignments" WHERE "assignments"."name" = 'homework1' LIMIT 1
there's no Update anywhere.... what am I doing wrong?
UPDATE
So I found out that I could just reference the join table directly to edit the links. Something along the lines of:
proj = p.projects.first
proj.assignment_id = 12
proj.save!
If you just want a reference to the object, then you need to edit your migration scripts (db/migrate). An example:
def self.up
create_table :configurations do |t|
t.string :name
t.references :project # This store just the id of the object.
t.timestamps
end
end
Don't forget to type:
rake db:migrate

Resources