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
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
I have a column/foreign key, resolver_id, that I want to be able to have null values (ie: Rails Migration to make a column null => true). Let's say I have the following line in my migration:
def
change_column_null :bugs, :resolver_id, true
end
However, after running a successful migration (ie, generate the migration and run rails db:migrate), the schema remains unchanged, besides the version number:
t.integer "resolver_id"
whereas I am expecting:
t.integer "resolver_id" , null: true
Is there something I'm missing?
I've also tried using just change_column like so:
change_column :bugs, :resolver_id, :integer, null: true
However, this is still not reflected in the schema. The rails g migration and db:migrate work just fine, and the version number in the schema matches the latest migration.
For reference, here is my schema:
ActiveRecord::Schema.define(version: 20170502203934) do
create_table "bugs", force: :cascade do |t|
t.string "name"
t.text "error_msg"
t.text "description"
t.text "causes"
t.boolean "resolved"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "user_id"
t.integer "resolver_id"
t.index ["resolver_id"], name: "index_bugs_on_resolver_id"
t.index ["user_id"], name: "index_bugs_on_user_id"
end
create_table "users", force: :cascade do |t|
t.string "username"
t.string "first_name"
t.string "last_name"
t.string "email"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "password_digest"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["username"], name: "index_users_on_username", unique: true
end
end
If relevant, the resolver_id foreign key is a reference a User model, ie:
class Bug < ApplicationRecord
# Associations
belongs_to :user
belongs_to :resolver, class_name: 'User'
end
null: true is the default behavior. You will never see it in your schema, you will see either null: false or nothing.
my app has 3 models, defined as follow:
class User < ActiveRecord::Base
has_many :vehicles, dependent: :destroy
has_one :insurance, through: :vehicle
end
class Vehicle < ActiveRecord::Base
belongs_to :user
has_one :insurance, dependent: :destroy
end
class Insurance < ActiveRecord::Base
belongs_to :vehicle
end
The resulting migration does not set any foreign keys for my insurances table. I expected to have two foreign keys, something like user_id and vehicle_id.
The resulting schema.rb file looks like this:
ActiveRecord::Schema.define(version: 20160314141604) do
create_table "insurances", force: :cascade do |t|
t.string "name"
t.date "issue_date"
t.date "expiry_date"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", default: "", null: false
t.string "encrypted_password", default: "", null: false
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0, null: false
t.datetime "current_sign_in_at"
t.datetime "last_sign_in_at"
t.string "current_sign_in_ip"
t.string "last_sign_in_ip"
t.string "confirmation_token"
t.datetime "confirmed_at"
t.datetime "confirmation_sent_at"
t.string "unconfirmed_email"
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", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
create_table "vehicles", force: :cascade do |t|
t.text "name"
t.date "matriculation_date"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "vehicles", ["user_id"], name: "index_vehicles_on_user_id"
end
Why insurances table has no foreign keys? Thank you
Run the following migrations:
rails g migration AddUserIDToInsurances user:references
rails g migration AddVehicleIDToInsurances vehicle:references
Then run rake db:migrate. This should add the two foreign keys you mentioned to your insurances table.
You have to specifically set the association keys in a migration. If you create a new migration and add:
add_column :vehicles, :user_id, :integer
add_column :insurances, :user_id, :integer
add_index :vehicles, :user_id
add_index :insurances, :user_id
# or whatever columns and indexes you need...
Rails gives you the has_one has_many and belongs_to methods to associate models conveniently with ActiveRecord, but the keys are not auto-generated unless you deliberately configure them in a migration file.
rails not null / unique in migrations doesn't trigger error :S
class CreateDeditProjects < ActiveRecord::Migration
def change
create_table :dedit_projects do |t|
t.string :name, :null => false
t.string :uid, :unique => true
t.boolean :status
t.timestamps null: false
end
end
end
empty name doesn't trigger error. Neither does duplication of uid.
This is what I see in schema.db
ActiveRecord::Schema.define(version: 20150410105216) do
create_table "dedit_projects", force: :cascade do |t|
t.string "name", null: false
t.string "uid"
t.boolean "status"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
hm, I need to add indexes somewhere I guess? Shouldn't that be automatic?
Not null problem is bogus though.
Rails automatically adds index on id and references and maybe on some other types. If You want to add new index, You can create a migration:
def change
add_index :dedit_projects, :uid, unique: true
end
You can also use validations validates_uniqueness_of and validates_presence_of in models. Although I don't understand why doesn't it work as it is :)
Uniqueness is a property of the index so you need either a separate call to add_index or write it like so
create_table :dedit_projects do |t|
t.string :uid, index: {unique: true}
...
end
I followed the documentation, manually creating the migration file and the models. I already had a User model in my app:
user.rb:
class User < ActiveRecord::Base
authenticates_with_sorcery!
validates :name, presence: true
validates :password, length: { minimum: 4 }
validates :password, confirmation: true
validates :password_confirmation, presence: true
validates :email, uniqueness: true
has_many :testimonials
has_many :materials
groupify :group_member
groupify :named_group_member
end
class Assignment < ActiveRecord::Base
groupify :group_member
end
group.rb:
class Group < ActiveRecord::Base
groupify :group, members: [:users, :assignments], default_members: :users
end
GroupMembership.rb:
class GroupMembership < ActiveRecord::Base
groupify :group_membership
end
schema.rb:
ActiveRecord::Schema.define(version: 20150301133633) do
create_table "group_memberships", force: :cascade do |t|
t.string "member_type"
t.integer "member_id"
t.integer "group_id"
t.string "group_name"
t.string "membership_type"
end
add_index "group_memberships", ["group_id"], name: "index_group_memberships_on_group_id"
add_index "group_memberships", ["group_name"], name: "index_group_memberships_on_group_name"
add_index "group_memberships", ["member_id", "member_type"], name: "index_group_memberships_on_member_id_and_member_type"
create_table "groups", force: :cascade do |t|
t.string "type"
end
create_table "materials", force: :cascade do |t|
t.string "level"
t.text "description"
t.string "link"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "title"
t.integer "user_id"
end
add_index "materials", ["user_id"], name: "index_materials_on_user_id"
create_table "testimonials", force: :cascade do |t|
t.text "content"
t.integer "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "testimonials", ["user_id", "created_at"], name: "index_testimonials_on_user_id_and_created_at"
add_index "testimonials", ["user_id"], name: "index_testimonials_on_user_id"
create_table "tests", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "users", force: :cascade do |t|
t.string "email", null: false
t.string "crypted_password"
t.string "salt"
t.datetime "created_at"
t.datetime "updated_at"
t.string "name"
end
add_index "users", ["email"], name: "index_users_on_email", unique: true
end
I run my console in sandbox mode and do:
group = Group.new
user = User.new
group.add user
And my console spews out:
NameError: uninitialized constant User::GroupMembership
What's going on?
SOLVED: The class GroupMembership.rb is written in camel case, so the filename needs to be:
group_membership.rb