I am trying to add seller_id to my items model in a migration by doing the following:
rails g model Item title:string description:text price:bigint status:integer published_date:datetime seller:belongs_to
This didn't work of course...
ultimately I want to see seller_id in my items table, which relates the item model to the user model.
Presently my migration looks like this:
class CreateItems < ActiveRecord::Migration
def change
create_table :items do |t|
t.string :title
t.text :description
t.bigint :price
t.integer :status
t.datetime :published_date
t.belongs_to :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
How can I modify this migration to create a foreign key "seller_id" that relates to the user model.
How can I auto generate a migration that includes edits from 1., from the console? I.E modify the rails g model ... to generate the migration I need.
Huge Thanks!
Just explicitly define what you want in the migration file and add the necessary relation to your model, instead of t.belongs_to you could just use:
t.integer :seller_id, index: true, foreign_key: true
and in your models you could go about this a few ways, if you also want reference your relation as seller on item instances then in the Item class make the relation:
belongs_to :seller, class_name: 'User'
and in the User class:
has_many :items, foreign_key: :seller_id
or if you want to reference it as user from the items then:
belongs_to :user, foreign_key: :seller_id
In terms of editing the generator, that is the default way that a model generator works and in my opinion I like to keep the defaults the way they are. However I do recommend creating your own rails generators or your own rake tasks for when you want to create some custom / special case stuff. For looking at creating your own generator I would point you to the ruby on rails official guide for creating generators to get you started: http://guides.rubyonrails.org/generators.html
Inside your migration you can use
t.belongs_to :seller, foreign_key: {to_table: :users}
If you don't want it to be specifically called seller_id then what you have is enough. Once you run your migration using rake db:migrate you will see the actual table structure in your db/schema.rb file with a foreign key user_id
You can simply edit the generated migration file in your favorite text editor to edit the migration before running rake db:migrate
Related
If I have command and model like this:
Command
$ bin/rails generate model User name:string firstname:string
$ bin/rails genarate model Profile username:string password:string
model generated by rails
class User < ApplicationRecord
has_one :profile
end
Class Profile < ApplicationRecord
belongs_to :user
end
Is the 1:1 association in migrate folder generated automatically by rails or I have to implement it myself?
The migration file for Profiles (class CreateProfiles < ActiveRecord::Migration) should have a foreign key declaration like this:
t.references :user, null: false, foreign_key: true
whole code:
create_table :profiles do |t|
t.string :username
t.string :password
t.references :user, null: false, foreign_key: true
t.timestamps
end
That can also be generated automaticaly with the generator command by adding user:references like so:
$ bin/rails genarate model Profile username:string password:string user:references
You can also refer to the Active Record Migrations guide and search for "references" on that page.
ActiveRecord::Associations::ClassMethods does not create database columns automatically. That would actually be a very risky and undesirable feature. You do not want your Rails server to change the DB schema at runtime. Thats what migrations are for.
You can however use the references "type" in the generators (also aliased as belongs_to) which creates a a foreign key column as well as an assocation in the model:
bin/rails g model Profile username:string password:string user:references
class CreateProfiles < ActiveRecord::Migration[7.0]
def change
create_table :profiles do |t|
t.string :username
t.string :password
t.references :user, null: false, foreign_key: true
t.timestamps
end
end
end
class Profile < ApplicationRecord
belongs_to :user
end
There is no equivilent for adding has_one or has_many assocations from generators as they don't actually correspond to a database column on this model.
If you need to add a foreign key column to the table after the fact you do it by generating a migration:
bin/rails g migration AddUserIdToProfiles user:refereces
I want to add to the table Orders two fields: seller_id and buyer_id that will reference to the Users Table.
I am not sure what is the best way to do this?
rails g migration AddUsersToOrder seller_id:integer buyer:integer
I can run this command, but as I see it this is not a good solution, because this command doesn't 'connect' the two tables, it just creates two integer fields without a FOREIGN KEY constraint. In other words the reference wouldn't be created, but definitely will work.
There is a pretty good article (but quite old from 2008) with the same example but it doesn't generate migration and it focuses on Model.
Also #Joe Kennedy wrote a very good solution for a similar question. But as I said, I think this will not create the reference.
rails g migration AddUserRefToOrder seller_id:references
This is the best way to do it, but the problem is that we can't create another migration for buyer this way.
I am wondering what should I do am I missing something?
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.
I wan to make simple has_many and belongs_to relation between my models.
I have User model and Quote model. And each quote belongs to user and evry user has many quotes.
I've studied this: http://guides.rubyonrails.org/association_basics.html
But I still don't know what about foreign key. Does Rails do it automatically in background (when I add has_many and belongs_to) or should I add manually migration with user_id column added to Quotes table?
Should I have user_id column in my db_schema?
The foreign keys should be defined in your migration. If you use the generator, Rails will generate a migration, which maybe looks like the following:
class CreateQuotes < ActiveRecord::Migration
def change
create_table :quotes do |t|
t.string :title, null: false
t.text :content, null: false
t.references :user
t.timestamps
end
end
end
The statement t.references :user will generate your foreign key column, which is called user_id in this case.
Here is a quote from the Rails Guides:
Using t.integer :supplier_id makes the foreign key naming obvious and explicit. In current versions of Rails, you can abstract away this implementation detail by using t.references :supplier instead.
I created a many-to-many relationship in rails, here's my models and migrations
class Channel < ActiveRecord::Base
has_and_belongs_to_many :packages
validates_presence_of :name
end
class Package < ActiveRecord::Base
has_and_belongs_to_many :channels
validates_presence_of :name
end
class CreateChannelsPackages < ActiveRecord::Migration
def change
create_table :channels_packages, :id => false do |t|
t.references :channel
t.references :package
t.timestamps
end
add_index :channels_packages, :channel_id
add_index :channels_packages, :package_id
end
end
Then i have a multiple select, but when i try to save i get this error
SQLite3::ConstraintException: constraint failed: INSERT INTO "channels_packages" ("package_id", "channel_id") VALUES (1, 1)
I tried to remove the indexes from the migration but it didn't solve it, did somebody else have this problem?
Btw i'm using Rails 3.2.6 and sqlite3 1.3.6
I think gabrielhilal's answer is not quite correct: use of extra attributes in the join table is deprecated, thus you need to remove the timestamp in your migration, then it should work just fine with the has_and_belongs_to_many wich itself is not deprecated.
If you do need additional attributes in your join table, though, has_many :through is the way to go.
There is also another question with good answers on this topic:
Rails migration for has_and_belongs_to_many join table
I don't know if it is the reason of your problem, but the has_and_belongs_to_many association is deprecated.
According to the Rails Guide:
The use of extra attributes on the join table in a has_and_belongs_to_many association is deprecated. If you require this sort of complex behavior on the table that joins two models in a many-to-many relationship, you should use a has_many :through association instead of has_and_belongs_to_many.
I know that you are not adding any extra attribute to the join table, but try changing your migration to the below, which I think is the default:
class CreateChannelPackageJoinTable < ActiveRecord::Migration
def change
create_table :channels_packages, :id => false do |t|
t.integer :channel_id
t.integer :package_id
t.timestamps
end
end
end
I have two models Artist and Painting.
I added the following migration and executed rake db:migrate
class AddArtistReferenceToPaintings < ActiveRecord::Migration
self.up do
change_table :paintings do |t|
t.references :artist
end
end
end
This doesn't change anything in the database. What am I doing wrong?
Seems correct .
Did you already run this migration and added this latter ? If yes then create new one OR delete version from schema_migrations .
Way :
To add a foreign key column
change_table(:suppliers) do |t|
t.references :company
end
It creates a company_id(integer) column
To add a polymorphic foreign key column
change_table(:suppliers) do |t|
t.belongs_to :company, :polymorphic => true
end
It creates company_type(varchar) and company_id(integer) columns .
For further detail refer the link.
Try
t.belongs_to :artist
instead
t.references :artist
But your variant should work too, if you test in irb console. Run 'reload' to update.