I generate a table by using
rails generate model Relationship follower_id:integer followed_id:integer
Then, I add my index as follows
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :followed_id
t.timestamps null: false
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
After that I ran rake db:migrate
rake db:migrate
== 20170922165845 CreateRelationships: migrating ==============================
-- create_table(:relationships)
-> 0.0010s
== 20170922165845 CreateRelationships: migrated (0.0011s) =====================
Why is it not migrating index?
schema.rb
ActiveRecord::Schema.define(version: 20170922181915) do
#...
create_table "relationships", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
#...
end
I ran your commands in a brand new rails (version 4.2 and ruby 2.3.4) project and everything works as expected. I would rake db:rollback and delete the files and try it again.
My Steps:
I ran rails generate model Relationship follower_id:integer followed_id:integer
Then edited the newly created migration to add indexes as follows:
class CreateRelationships < ActiveRecord::Migration
def change
create_table :relationships do |t|
t.integer :follower_id
t.integer :followed_id
t.timestamps null: false
end
add_index :relationships, :follower_id
add_index :relationships, :followed_id
add_index :relationships, [:follower_id, :followed_id], unique: true
end
end
ran rake db:migrate
opened schema.rb and had NOTICE the indexes are outside the create_table block:
# encoding: UTF-8
# This file is auto-generated from the current state of the database. Instead
# of editing this file, please use the migrations feature of Active Record to
# incrementally modify your database, and then regenerate this schema definition.
#
# Note that this schema.rb definition is the authoritative source for your
# database schema. If you need to create the application database on another
# system, you should be using db:schema:load, not running all the migrations
# from scratch. The latter is a flawed and unsustainable approach (the more migrations
# you'll amass, the slower it'll run and the greater likelihood for issues).
#
# It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema.define(version: 20170922191347) do
create_table "relationships", force: :cascade do |t|
t.integer "follower_id"
t.integer "followed_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "relationships", ["followed_id"], name: "index_relationships_on_followed_id"
add_index "relationships", ["follower_id", "followed_id"], name: "index_relationships_on_follower_id_and_followed_id", unique: true
add_index "relationships", ["follower_id"], name: "index_relationships_on_follower_id"
end
Related
So I am currently assigned a task in Ruby which I have never used before and I've run into a very strange problem. I have this migration for a model message, which has an index on chat_id and number.
class CreateMessages < ActiveRecord::Migration[5.2]
def change
create_table :messages do |t|
t.references :chat, foreign_key: true, null: false
t.integer :number, null: false
t.string :body
#t.index [:chat_id, :number] doesn't work
t.timestamps
end
#add_index :messages, [:chat_id, :number] #doesn't work either
end
end
The end schema of both those migrations when running rails db:migrate is this
create_table "messages", force: :cascade do |t|
t.integer "chat_id", null: false
t.integer "number", null: false
t.string "body"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["chat_id", "number"], name: "index_messages_on_chat_id_and_number"
t.index ["chat_id"], name: "index_messages_on_chat_id"
end
Obviously, the second created index is completely useless. How do I prevent this from occuring and create only one index when migrating?
Ruby 2.7.6
Rails 5.2.8.1
This line is adding the index you wish to remove. It accepts an index parameter which defaults to true, change it to:
t.references :chat, foreign_key: true, null: false, index: false
Description
I'm on Ruby version 2.6.6 and Ruby on Rails version 6.0.3.2.
Models
Book
Author
Associations
A Book belongs to an Author.
An Author has many Books.
Goal
Remove the Author model and add it as a column to a Book (of type string).
What I've Done
I created and ran 3 migrations, in the following order:
AddAuthorToBooks
class AddAuthorToBooks < ActiveRecord::Migration[6.0]
def change
add_column :books, :author, :string
end
end
DropAuthors
class DropAuthors < ActiveRecord::Migration[6.0]
def change
drop_table :authors do |t|
t.string "full_name", null: false
t.timestamps null: false
end
end
end
RemoveAuthorForeignKeyFromBooks
class RemoveAuthorForeignKeyFromBooks < ActiveRecord::Migration[6.0]
def change
remove_foreign_key :books, :authors
end
end
Schema
Unfortunately, I don't have the schema before I ran the migrations. (I tried checking out an older commit, but the schema file stubbornly refuses to change.)
Here is the current version:
ActiveRecord::Schema.define(version: 2020_08_11_125724) do
create_table "books", force: :cascade do |t|
t.string "title"
t.text "description"
t.string "cover_url"
t.decimal "price"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "author_id", null: false
t.string "author"
t.index ["author_id"], name: "index_books_on_author_id"
end
create_table "books_genres", id: false, force: :cascade do |t|
t.integer "book_id", null: false
t.integer "genre_id", null: false
t.index ["book_id", "genre_id"], name: "index_books_genres_on_book_id_and_genre_id"
t.index ["genre_id", "book_id"], name: "index_books_genres_on_genre_id_and_book_id"
end
create_table "genres", force: :cascade do |t|
t.string "name"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
create_table "reviews", force: :cascade do |t|
t.string "username"
t.decimal "rating"
t.text "body"
t.integer "book_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.index ["book_id"], name: "index_reviews_on_book_id"
end
add_foreign_key "reviews", "books"
end
Problem
The author table has been removed, the add_foreign_key "books", "authors" (I think that's how it went) is gone, too, but the author_id stubbornly remains in the books table.
Furthermore, there's an integer author_id and an index of the same name.
What I'm Planning
I thought of just deleting these 2 columns with another migration, but I don't know if that would...
...fix the issue and erase the old Author model completely,
...and that it's the clean/recommended way of doing things. If needed, I could roll back the migrations and try a better method.
About the unusual `schema.rb` behavior
I tried checking out an older commit, but the schema file stubbornly refuses to change.
This is quite unusual. Do verify that you're tracking the db/schema.rb file using git. If it is tracked, there's no reason why checking out an older commit shouldn't return it to the older state. At that point, you should be able to:
$ rails db:drop
$ rails db:create
$ rails db:schema:load
...to load the old schema into the database. Then, you should be able to return to the latest code with git, and run pending migrations after the date at which the older schema was created.
About a cleaner way to implement this
Before writing the below migration, the first step would be to remove any existing relationship written in the Book class. For example:
# app/models/book.rb
class Book < ApplicationRecord
# The line below should be deleted! Otherwise, it will probably interfere
# with the `book.update!(author: ...)` line in the migration.
belongs_to :author
end
I've taken to writing related migrations in a single file, since they're all related. To me, this looks like:
class MoveAuthorToBooks < ActiveRecord::Migration[6.0]
class Author < ApplicationRecord
end
class Book < ApplicationRecord
end
def up
# Start by adding a string column.
add_column :books, :author, :string
# Let's preserve existing author names.
Book.all.each do |book|
author = Author.find(book.author_id)
book.update!(author: author.name)
end
# Now that the names have been moved to the books table, we don't
# need the relationship to `authors` table anymore. This should
# also delete any related foreign keys - manual foreign key deletion
# should not be required.
remove_column :books, :author_id
# Alternative: If you'd created the `authors_id` column using the
# `add_reference` command, then it's probably best to use the opposite
# `remove_reference` command.
#
#remove_reference :books, :author, index: true, foreign_key: true
# Finally, remove the `authors` table.
drop_table :authors
end
def down
# This can be technically be reversed, but that'll need some more code that
# reverses the action of the `up` function, and it may not be needed.
raise ActiveRecord::IrreversibleMigration
end
end
class CreateMessages < ActiveRecord::Migration[5.2]
def change
create_table :messages do |t|
t.text :body
t.integer :user_id
t.timestamps
end
end
end
After running rails db:migrate my schema looks like this...
ActiveRecord::Schema.define(version: 2020_03_20_063104) do
create_table "messages", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "password_digest"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
I am wondering where the t.text "body" is and where the t.integer "user_id"is and why it isn't showing up in my schema under messages table.
I have checked migration status and all migrations have been ran.
If you ran something like this in your migration file
class CreateMessages < ActiveRecord::Migration[5.2]
def change
create_table :messages do |t|
t.timestamps
end
end
end
Messages table is created and after this you can't create another migration with create_table :messages. Like #Marek Lipka wrote on comments. Either you need to rollback your CreateMessages migration and chance file and run your migration again. Or you need to write another migration to change existing table like this.
class AddBodyAndUserIdToMessages < ActiveRecord::Migration[5.2]
def change
add_column :messages, :body, :text
add_column :messages, :user_id, :integer
end
end
I'm trying to add validation at the database level. For the model file it was no problem but now for the migration file, I just don't get how to do this. I am using SQLite for development (following Hartl's tutorial but adapting the instructions for my own app).
I have the two following migration files:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email, null: false
t.string :email_confirm, null: false
t.string :username, null: false
t.text :bio
t.string :location, null: false
t.boolean :activated, default: false
t.datetime :activated_at
t.timestamps null: false
end
end
end
and:
class AddIndexToUsersEmailAndUsername < ActiveRecord::Migration
def change
add_index :users, :email, unique: true
add_index :users, :username, unique: true
end
end
But the validations do not end up in schema.rb:
ActiveRecord::Schema.define(version: 20150410200022) do
create_table "Users", force: :cascade do |t|
t.string "email"
t.string "email_confirm"
t.string "username"
t.text "bio"
t.string "location"
t.boolean "activated"
t.datetime "activated_at"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
add_index "users", ["username"], name: "index_users_on_username", unique: true
end
So null:false and default:false are not in schema.rb and apparantly Rails is not implementing these validations. How should I implement these validations in the migration file?
I've also tried using change_column_null and change_column_default instead but with the same result/schema.rb.
Rails does let you provide null and default options to columns in migrations.
After editing the user migration file, have you rerun it?
You can do a rake db:rollback followed by a rake db:migrate
If you had run CreateUsers and AddIndexToUsersEmailAndUsername for last, You should do a rollback with two steps like rake db:rollback STEP=2
I read a lot about it but i still can't get rid of one table that I have in my db:
create_table "votes", force: true do |t|
t.integer "votable_id"
t.string "votable_type"
t.integer "voter_id"
t.string "voter_type"
t.boolean "vote_flag"
t.string "vote_scope"
t.integer "vote_weight"
t.datetime "created_at"
t.datetime "updated_at"
end
add_index "votes", ["votable_id", "votable_type", "vote_scope"],
name: "index_votes_on_votable_id_and_votable_type_and_vote_scope"
add_index "votes", ["voter_id", "voter_type", "vote_scope"],
name: "index_votes_on_voter_id_and_voter_type_and_vote_scope"
I don't have a migration that create this table. But I think this table comes from a previous installation of the acts_as_votable gem. But I might have deleted this migration manually...
I tried to drop_table as follows, but it's not working. Even if I have this migration file, the votes table is still there:
class DropVotesTable < ActiveRecord::Migration
def up
drop_table :votes
end
def down
raise ActiveRecord::IrreversibleMigration
end
end
What should I do to delete this table from my schema.rb file?
EDIT: solution
Even after running the migration the table was still in my schema.rb so I used the rails console:
rails c
ActiveRecord::Migration.drop_table(:votes)
rake db:migrate
And this table finally disappeared.