how to add index when column is already exists in my db?
when i create migration like this :
class AddIndexToUsers < ActiveRecord::Migration
def change
remove_column :users, :id_number if index_exists?(:id_number)
add_column :users, :id_number, :string
add_index :users, :id_number, unique: true
end
end
i get error like this when create object with same id_number :
SQLite3::ConstraintException: column id_number is not unique: INSERT INTO "users" ("bank_account", "birth_date", "confirmed", "confirmed_at", "created_at", "credit_card_id", "email", "encrypted_password", "facebook_id", "google_id", "id_issuing_country", "id_number", "last_name", "name", "updated_at") VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
class AddIndexToUsers < ActiveRecord::Migration
def change
remove_index :users, :id_number if index_exists?(:id_number)
add_index :users, :id_number, unique: true
end
end
This wiil fail if your already duplicate id_number entries in the :users table. You will have to remove the duplicates first if any
Think about your question very carefully...Why do you want to add something that already exists? If you want to generate a model in a relational database and not have an ID to it, you need to pass in :id => false
create_table(:categories_suppliers, id: false) do |t|
t.column :category_id, :integer
t.column :supplier_id, :integer
end
If you're mistaken, and your trying to ensure there is an ID to your model, you can test by going to your terminal and typing rails c
#users = User.all
#users.first.id
To validate this, you will need to actually have some data in your table. Of course, you'll know, because typing #users into your terminal will return an empty array.
Related
I am developing a simple app where a user can add a subject to a cart. But there is an error about not null constraint.
The error message browser shows is like that.
SQLite3::ConstraintException: NOT NULL constraint failed: items.title: INSERT INTO "items" ("image", "created_at", "updated_at") VALUES (?, ?, ?)
I've tried deleting not null constraint in schema.rb. But the error message is still on. So, what should I do?
Schema:
create_table "items", force: :cascade do |t|
t.string "image", null: false
t.string "title", null: false
t.string "description", null: false
t.string "stock", null: false
t.string "price", null: false
t.integer "status", limit: 1, default: 0, null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
Controller:
class SellController < ApplicationController
def new
#item = Item.new
end
def confirm
#item = Item.new(title: params[:title],price: params[:price],stock: params[:stock],description: params[:description],image: "default_item.jpg")
render :new if #item.invalid?
end
def create
#item = Item.new(title: params[:title],price: params[:price],stock: params[:stock],description: params[:description],image: "default_item.jpg")
##item = Item.new(item_params)
if params[:image]
#item.image = "#{#item.id}.jpg"
image = params[:image]
File.binwrite("public/item_images/#{#item.image}", image.read)
end
if params[:back]
format.html { render :new }
elsif #item.save
flash[:notice] = "your item data is saved."
redirect_to("/sell/complete")
else
render("sell/new")
end
end
def complete
end
end
I expect item data be saved and the page on browser is changed to thanks page.
Take a closer look at your error message:
SQLite3::ConstraintException: NOT NULL constraint failed: items.title: INSERT INTO "items" ("image", "created_at", "updated_at") VALUES (?, ?, ?)
It says that failed not null constraint on items.title column. And indeed, you don't provide title in your insert statement. It means that they were not passed to Item.new in your controller code.
I can see two solutions - if you want to keep all those constraints (you have multiple not-null columns in your table), you should also add active record presence validations. They will prevent calling insert statement to the database if values are missing and they will give you nice error messages in #item.error.
If you can allow those columns to be nullable, you can drop the constraint in a migration:
change_column_null :items, :title, false
You can also rollback the migration that created items table and edit this migration to avoid setting NOT NULL constraint there.
schema.rb is generated when you run migrations and can be used to load schema into a database. It's not done automatically though, you need to run a proper rake task (rake db:schema:load). It's not advisable to edit this file manually, as it's auto-generated and your changes will be automatically overwritten.
I have the following models:
class Test < ApplicationRecord
end
class Exam < Test
end
class Practice < Test
has_many :relations
has_many :questions, through: :relations
end
class Relation < ApplicationRecord
belongs_to :practice
belongs_to :question
end
class Question < ApplicationRecord
has_many :relations
has_many :practices, through: :relations
end
And this is my schema:
create_table "questions", force: :cascade do |t|
t.string "title"
t.text "text"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
create_table "relations", force: :cascade do |t|
t.integer "practice_id"
t.integer "question_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["practice_id"], name: "index_relations_on_practice_id"
t.index ["question_id"], name: "index_relations_on_question_id"
end
create_table "tests", force: :cascade do |t|
t.string "name"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
When I try in rails console:
#p = Practice.new
#p.save
#q = Question.new
#q.save
Practice.last.questions << Question.last
I get this error:
Question Load (0.2ms) SELECT "questions".* FROM "questions" ORDER BY "questions"."id" DESC LIMIT ? [["LIMIT", 1]]
(0.1ms) begin transaction
SQL (0.4ms) INSERT INTO "relations" ("practice_id", "question_id", "created_at", "updated_at") VALUES (?, ?, ?, ?) [["practice_id", 2], ["question_id", 1], ["created_at", "2017-10-26 06:09:42.581082"], ["updated_at", "2017-10-26 06:09:42.581082"]]
(0.1ms) rollback transaction
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: main.practices: INSERT INTO "relations" ("practice_id", "question_id", "created_at", "updated_at") VALUES (?, ?, ?, ?)
The error is obvious it doesn't find the table practices but how can I fix this? I don't understand how to specify to use the table tests, instead of practices. Any help is appreciated
There should be no practices table since Practice inherits Test and you want to use STI pattern.
I've repeated your models and schema on my machine and it works as expected. So either you have some problems with SQLite or with things like spring.
Calm down spring with spring stop and then try to recreate db with rails db:reset and make sure there are no errors.
Moreover I hope it is just example codes and in real life you don't name sql relation as relations =)
I originally had this migration:
def change
add_column :users, :provider, :string, null: false, default: "email"
add_column :users, :uid, :string, null: false, default: ""
add_index :users, [:uid, :provider], unique: true
end
But in my application, users can sign in with both omniauth and username and password without oauth authentication. As a result, in many situations, uid and provider will be null. So I created the following migration:
def change
change_column_default :users, :provider, nil
change_column_default :users, :uid, nil
end
But when I try to set the provider or uid to nil in Rails, I get a PG::NotNullViolation: ERROR:
u = User.first
u.provider = nil
u.save!
(0.4ms) BEGIN
SQL (1.5ms) UPDATE "users" SET "updated_at" = $1, "provider" = $2 WHERE "users"."id" = $3 [["updated_at", 2017-08-16 00:01:34 UTC], ["provider", nil], ["id", 1]]
(0.5ms) ROLLBACK
ActiveRecord::StatementInvalid: PG::NotNullViolation: ERROR: null value in column "provider" violates not-null constraint
DETAIL: Failing row contains
It appears unique: true in the migration prevents setting null values. How can I get around this?
You have set the columns to null: false in your first migration which is causing the PG::NotNullViolation exception. That needs to be set to true to allow null values in both the columns. Writing a new migration to set null: true should resolve the issue as follows.
def change
change_column_null :users, :provider, true
change_column_null :users, :uid, true
end
Also below index may not work(RecordNotUnique exception raises as it is set unique: true) as you will have multiple rows having both uid and provider with null values. So this index need to be dropped.
add_index :users, [:uid, :provider], unique: true
In addition to this:
def change
change_column :users, :provider, :string, null: true
change_column :users, :uid, :string, null: true
remove_index :users, [:uid, :provider]
end
which would subsequently allow null values but eliminate the two field constraint at the database-level, I did this at the model-level:
validates :provider, uniqueness: { scope: :uid }, allow_nil: true
I am "banging" my head against a wall trying to figure this out. I decided to allow users to log in through twitter, Google,my site or Facebook. The problem is that twitter does not provide emails, so I am trying to add users by username. The problem with that is devise keeps checking for email and when I don't require it, I get PG::Error: ERROR: duplicate key value violates unique constraint "index_users_on_email" DETAIL: Key (email)=() already exists. : INSERT INTO "users" ("created_at", "name", "provider", "uid", "updated_at", "username") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id".
I don't know what I should do. I created a username column in my database, and I made sure to add this to my devise.rb intializer, config.authentication_keys = [ :username ]
I think that's because default Devise installation creates unique key on email coulmn. In your case it's empty, hence second user doesn't have unique one (because empty string is taken by the first user).
Migration in my case
class AllowNullEmail < ActiveRecord::Migration
def up
remove_index :users, :email
change_column :users, :email, :string, :null => true
end
def down
change_column :users, :email, :string, :null => false
add_index :users, :email, unique: true
end
end
I have a User model with a draft_record boolean column which has a default of true. When creating records they are created with draft_record: false rather than true. This worked when the field was called draft however then the draft association and draft attribute assignment methods clashed resulting in the draft attribute being unsettable. Am I doing something wrong? I have had this problem before and just worked around it by reverting to what worked.
Ruby: 1.9.3-p327
Ruby on Rails: 3.2.12
DBMS: Postgres
Relevant migration:
class AddDraftColumnToUsers < ActiveRecord::Migration
def self.up
add_column :users, :draft_record, :boolean, default: true
add_column :users, :draft_id, :integer
add_column :users, :current_id, :integer
end
def self.down
...
end
end
Resultant schema
ActiveRecord::Schema.define(:version => 20130303002123) do
create_table "users", :force => true do |t|
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
t.string "name"
t.boolean "draft_record", :default => true
t.integer "draft_id"
t.integer "current_id"
end
end
Creating a user object:
Loading development environment (Rails 3.2.12)
1.9.3-p327 :001 > u = User.create(name: "Jon")
(0.0ms) begin transaction
SQL (28.8ms) INSERT INTO "users" ("created_at", "current_id", "draft_id", "draft_record", "name", "updated_at") VALUES (?, ?, ?, ?, ?, ?) [["created_at", Sun, 03 Mar 2013 00:42:04 UTC +00:00], ["current_id", nil], ["draft_id", nil], ["draft_record", false], ["name", "Jon"], ["updated_at", Sun, 03 Mar 2013 00:42:04 UTC +00:00]]
(0.7ms) commit transaction
=> #<User id: 1, created_at: "2013-03-03 00:42:04", updated_at: "2013-03-03 00:42:04", name: "Jon", draft_record: false, draft_id: nil, current_id: nil>
1.9.3-p327 :002 >
This problem was caused by a default_scope with the conditions hash set to draft_record: false. This forced any record being added through active record to set draft_record to false.
The default value is set at DB level. So the insert query will not generate the default. Check the records inserted if they have correct default value set.