How do I add migration with multiple references to the same model in one table? Ruby/Rails
You can do this simply with the add_column
method in your migrations and set up the proper associations in your classes:
class AddFields < ActiveRecord::Migration
def change
add_column :tickets, :image_1_id, :integer
add_column :tickets, :image_2_id, :integer
end
end
class Ticket < ActiveRecord::Base
belongs_to :image_1, :class_name => "Image"
belongs_to :image_2, :class_name => "Image"
end
class Image < ActiveRecord::Base
has_many :primary_tickets, :class_name => "Ticket", :foreign_key => "image_1_id"
has_many :secondary_tickets, :class_name => "Ticket", :foreign_key => "image_2_id"
end
This blog post, Creating Multiple Associations with the Same Table, goes into more detail.
Defining two references to the same column in another table
A database table cannot have two columns with the same name. This is what you'll need in order to get this to work. (I'm going to use home_team
and away_team
to help distinguish them, but obviously you could use team1
and team2
.)
rails g migration AddTeamsToMatch home_team_id:integer away_team_id:integer
This will generate a migration that looks like this:
class AddTeamsToMatch < ActiveRecord::Migration
def change
add_column :matches, :home_team_id, :integer
add_column :matches, :away_team_id, :integer
end
end
Then in your Team
model, you can have the following:
class Team < ActiveRecord::Base
has_many :home_matches, class_name: "Match", foreign_key: "home_team_id"
has_many :away_matches, class_name: "Match", foreign_key: "away_team_id"
# Get all matches
def matches
self.home_matches + self.away_matches
end
end
In your Match
model, you can have:
class Match < ActiveRecord::Base
belongs_to :home_team, class_name: "Team"
belongs_to :away_team, class_name: "Team"
# List both teams as array
def teams
[self.home_team, self.away_team]
end
end
Hopefully this helps.
Can I generate a migration with 2 fields that will reference on the same table?
Depends on how you want to interact with the things you can choose from 2 versions.
For tasks it was important to see who was the assigner/executor so I was able to sort the different (incoming/outgoing) tasks. So I could call user.assigned_tasks
or user.executed_tasks
.
For conversations I was not interested in interacting with that model. It does not matter who created it. I don't need conversation.sender
or conversation.recipient
. I need the messages (nested under conversation) to be identified based on the sender/recipient.
Version A
class Task < ActiveRecord::Base
belongs_to :assigner, class_name: "User"
belongs_to :executor, class_name: "User"
end
class User < ActiveRecord::Base
has_many :assigned_tasks, class_name: "Task", foreign_key: "assigner_id", dependent: :destroy
has_many :executed_tasks, class_name: "Task", foreign_key: "executor_id", dependent: :destroy
end
create_table "tasks", force: :cascade do |t|
t.integer "assigner_id"
t.integer "executor_id"
end
add_index "tasks", ["assigner_id"], name: "index_tasks_on_assigner_id", using: :btree
add_index "tasks", ["executor_id"], name: "index_tasks_on_executor_id", using: :btree
Version B
class User < ActiveRecord::Base
has_many :conversations, foreign_key: "sender_id", dependent: :destroy
end
class Conversation < ActiveRecord::Base
belongs_to :sender, class_name: "User", foreign_key: "sender_id"
belongs_to :recipient, class_name: "User", foreign_key: "recipient_id"
end
create_table "conversations", force: :cascade do |t|
t.integer "sender_id"
t.integer "recipient_id"
end
add_index "conversations", ["recipient_id"], name: "index_conversations_on_recipient_id", using: :btree
add_index "conversations", ["sender_id"], name: "index_conversations_on_sender_id", using: :btree
UPDATE:
If you run rails g model User
or rails g model Order
, migration will also be generated along test files, model file etc. This is the preferred way if the table does not exist yet. If table already exists and you wanna change it then you create only the migration which can be done 2 ways. The first is what you are talking about where u also pass the arguments, so when u open up the migration file the columns will already be there. But that is the hard way. You can just simply run rails g migration AddSomethingToThis
. Here the migration name does not matter, you should just choose something descriptive so you can easily recognize the migration file later on. Then you open up the migration file and you put there the columns you want. In my code u see the necessary foreign keys
. Besides that you can create price column etc., what you business logic needs. Then just run rake db:migrate
, which will change your db schema. You can always check out your current schema in schema.rb
. Never change the schema manually only by migrations.
Summing up:
- Create migration (with model if there is no table yet)
- Edit the migration file
- Run rake db:migrate so schema will be updated.
Update 2:
You can generate indexes easily by adding t.integer "recipient_id", index :true
in the migration file and then the the line that you highlighted will appear in schema.rb
(where I copied that part of my code from) after running rake db:migrate
. Check out some migration examples.
So indexes are about performance. With indexes the search in the db is faster in most cases. It's something like bookmark in a book. Usually you add it to foreign keys since those are the bridges between 2 models. If there are some attributes in a model which is queried heavily then you can add it to those as well. Don't add to too many lines. You can imagine what happens when you add let's say 20 bookmarks to a book.
Rails reference 2 columns from another model
This first argument after the has_many
needs to be different.
Something like this:
has_many :child_organization_organizations, class_name: "ChildOrganization", foreign_key: "organization_id", dependent: :destroy
has_many :child_organization_child_organizations, class_name: "ChildOrganization", foreign_key: "child_organization_id", dependent: :destroy
Rails: Multiple references to the same model error
Rails guesses based on the association, because you're referencing a table it can't determine from the association you'll have to add it yourself.
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.references :user, index: true, foreign_key: true
t.references :teammember, index: true
t.text :body
t.boolean :read, default: false
t.timestamps null: false
end
add_foreign_key :messages, :users, column: :teammember_id
end
end
Referencing the same model twice in Rails with a simple entity?
You can't actually use a single assocation here as ActiveRecord does not support assocations where the foreign key could be in one of two columns. Each has_one
/has_many
corresponds to a single foreign key column on the inverse side.
Instead you need one assocation on Address for each foreign key on Shipment:
class Shipment < ApplicationRecord
belongs_to :address_from,
class_name: 'Address',
inverse_of: :outgoing_shipments
belongs_to :address_to,
class_name: 'Address',
inverse_of: :incoming_shipments
end
class Address < ApplicationRecord
has_many :outgoing_shipments,
class_name: 'Shipment',
foreign_key: :address_from_id,
inverse_of: :address_from
has_many :incoming_shipments,
class_name: 'Shipment',
foreign_key: :address_to_id,
inverse_of: :address_to
end
While you could use has_one
here you should note that there is nothing preventing a address from having multiple shipments unless you add uniqueness constraints on shipments.address_from_id
and shipments.address_to_id
and validations. Not sure why you would want this though.
Multiple foreign keys referencing the same table in RoR
This sounds like a has_many relationship to me - put the customer_id in the Address table instead.
Customer
has_many :addresses
Address
belongs_to :customer
You can also provide a foreign key and class in the assoc declaration
Customer
has_one :address
has_one :other_address, foreign_key => "address_id_2", class_name => "Address"
Related Topics
Looping Through Bits in an Integer, Ruby
How to Enable C Extension Support in Jruby
What's the Point of Argv in Ruby
Circular Dependency Detected While Autoloading Constant User
How to Use Rspec's Should_Raise with Any Kind of Exception
How to Find Out Which Gem Has a Specific Dependency
How to Find the Key of the Largest Value Hash
Comparing Two Arrays Ignoring Element Order in Ruby
How to Compare Strings Ignoring the Case
In Ruby on Rails, After Send_File Method Delete the File from Server
Sum the Value of Array in Hash
How to Run Ruby and Git Commands in One Place on Windows
What's the Difference Between Colon ":" and Fat Arrow "=>"
Ruby: Why Does Puts Call To_Ary