why PG::UniqueViolation: ERROR: duplicate key value violates unique constraint? - ruby-on-rails

I have a model Post and each time a post is created I want a new instance of Moderation to be created at the same time.
So in post.rb I use the callback after_save :create_moderation
Then write a private method :
...
include Reportable
after_save :create_moderation
private
def create_moderation
self.create_moderation!(blog: Blog.first)
end
But when a proposal is created I get this error :
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "moderations_reportable" DETAIL: Key (reportable_type, reportable_id)=(Post, 25) already exists. : INSERT INTO "moderations" ("blog_id", "reportable_type", "reportable_id", "created_at", "updated_at", "blog_type") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
In reportable.rb I have :
has_one :moderation, as: :reportable, foreign_key: "reportable_id", foreign_type: "reportable_type", class_name: "Moderation"
Then few other methods for reportable object.
Note that this issue doesn't happen when I run the create method in console.
EDIT
create_table "moderations", id: :serial, force: :cascade do |t|
t.string "reportable_type", null: false
t.string "reportable_id", null: false
t.integer "blog_id", null: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "blog_type", null: false
t.string "upstream_moderation", default: "unmoderate"
t.index ["blog_id", "blog_type"], name: "moderations_blog"
t.index ["reportable_type", "reportable_id"], name: "moderations_reportable", unique: true
end
create_table "posts", id: :serial, force: :cascade do |t|
t.text "title", null: false
t.text "body", null: false
t.integer "feature_id", null: false
t.integer "author_id"
t.integer "scope_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "post_votes_count", default: 0, null: false
t.index ["body"], name: "post_body_search"
t.index ["created_at"], name: "index_posts_on_created_at"
t.index ["author_id"], name: "index_posts_on_author_id"
t.index ["feature_id"], name: "index_posts_on_feature_id"
t.index ["proposal_votes_count"], name: "index_posts_on_post_votes_count"
t.index ["title"], name: "post_title_search"
end

To fix the issue, we have to tell ActiveRecord to look at the sequence of the table:
ActiveRecord::Base.connection.reset_pk_sequence!('table_name')
Now ActiveRecord should have the correct sequence value, and should be able to assign new id's properly.
To resolve error
PG::UniqueViolation: ERROR: duplicate key value violates unique constraint "moderations_reportable" DETAIL: Key (reportable_type, reportable_id)=(Post, 25) already exists. : INSERT INTO "moderations" ("blog_id", "reportable_type", "reportable_id", "created_at", "updated_at", "blog_type") VALUES ($1, $2, $3, $4, $5, $6) RETURNING "id"
As error occurred on 'moderations' table.
Run the following from rails console fix
ActiveRecord::Base.connection.reset_pk_sequence!('moderations')
Thank you

Fix pkey sequences for all database:
ActiveRecord::Base.connection.tables.each do |table_name|
ActiveRecord::Base.connection.reset_pk_sequence!(table_name)
end

Looks like you've added a unique index to your database:
t.index ["reportable_type", "reportable_id"], name: "moderations_reportable", unique: true
With a unique index you will only be able to have one record with the same reportable_type and reportable_id. It's likely that you're trying to create a moderation for a reportable that already has a moderation.

ONLY FOR THOSE WHO ARE SEEING THIS WHILE RUNNING TESTS
One possible reason is, you have some migrations that create new data-records and you ran one of the following recently.
rake db:migrate RAILS_ENV=test
or
rake db:migrate:reset RAILS_ENV=test
Solution
You need to clean/wipeout your test-DB. Run this
rake db:reset RAILS_ENV=test
This will wipeout and generate schema from schema.rb.

I think this can also resolve your problem, Add this in your staging or preprod console ( I am using Heroku for this so I added in my heroku rails console)
connection = ActiveRecord::Base.connection
connection.execute("SELECT setval(pg_get_serial_sequence('moderations', 'id'), MAX(id)) FROM moderations;")
Check out this blog for more info and how this is been generated

Related

Heroku run rails db:migrate showing PG::DuplicateColumn: ERROR: column "created_at" of relation "blogs" already exists

When i try to run heroku run rails db:migrate, i get the following error
Running rails db:migrate on ⬢ sharley... up, run.6479 (Free)
I, [2021-01-03T23:13:45.240708 #4] INFO -- : Migrating to AddTimestampToBlogs (20201225171213)
== 20201225171213 AddTimestampToBlogs: migrating ==============================
-- add_timestamps(:blogs, {:null=>true})
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
PG::DuplicateColumn: ERROR: column "created_at" of relation "blogs" already exists
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `exec'
/app/bin/rails:4:in `<main>'
Caused by:
PG::DuplicateColumn: ERROR: column "created_at" of relation "blogs" already exists
/app/vendor/bundle/ruby/2.7.0/gems/activerecord-6.1.0/lib/active_record/connection_adapters/postgresql/database_statements.rb:47:in `exec'
/app/bin/rails:4:in `<main>'
Tasks: TOP => db:migrate
(See full trace by running task with --trace)
This is my schema file
Please, any help will be appreciated. Ive searched all over but to no avail.
ActiveRecord::Schema.define(version: 2020_12_29_225613) do
create_table "blogs", force: :cascade do |t|
t.string "name"
t.string "title"
t.string "content"
t.datetime "created_at", precenter code hereision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "user_id"
t.index ["user_id"], name: "index_blogs_on_user_id"
end
create_table "comments", force: :cascade do |t|
t.string "name"
t.text "body"
t.integer "blog_id", null: false
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.integer "post_id"
t.integer "user_id"
t.index ["blog_id"], name: "index_comments_on_blog_id"
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.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
t.string "name"
t.integer "user_id"
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "comments", "blogs"
end
This is my add timestamps to blog file
class AddTimestampsToBlogs < ActiveRecord::Migration[6.1]
def change
add_timestamps :blogs, null: true
# backfill existing record with created_at and updated_at
# values making clear that the records are faked
long_ago = DateTime.new(2020, 12, 29)
Blog.update_all(created_at: long_ago, updated_at: long_ago)
# change not null constraints
change_column_null :blogs, :created_at, false
change_column_null :blogs, :updated_at, false
end
end
Is there anything else needed?
You could add add_timestamps(:blogs, {:null=>true}) unless column_exists?(:blogs, :created_at) && column_exists?(:blogs, :updated_at). It looks like someone deployed a migration to fix that before or something and now you're trying as well. With the conditional, it will only run the add_timestamps migration method if there are no created or updated at columns
UPDATE
In the migration add the conditional. With the conditional, it will only run that step if the timestamp columns don't exist. The issue is that you or someone on your team has run the migration on heroku before then possibly rolled back locally and then redployed. The db still has that change, hence the error you're receiving. So if you add the conditional, it will add the time stamp columns only if they already don't exist on that table
def change
add_timestamps :blogs, null: true unless column_exists??(:blogs, :created_at) && column_exists?(:blogs, :updated_at)
# backfill existing record with created_at and updated_at
# values making clear that the records are faked
long_ago = DateTime.new(2020, 12, 29)
Blog.update_all(created_at: long_ago, updated_at: long_ago)
# change not null constraints
change_column_null :blogs, :created_at, false
change_column_null :blogs, :updated_at, false
end

Rails: Active Record prevents me from using my model in controller:

I've just start to creating new app from scratch and call a Pocket model from this controller:
class HomeController < ApplicationController
def index
end
def wallets_index
end
def wallets_show
end
def wallets_new
#pocket = Pocket.new
end
end
and this odd error appeared:
transaction is defined by Active Record. Check to make sure that you don't have an attribute or method with the same name.
my schema:
create_table "pockets", force: :cascade do |t|
t.string "address"
t.bigint "user_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.integer "transaction"
t.index ["user_id"], name: "index_pockets_on_user_id"
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.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["email"], name: "index_users_on_email", unique: true
t.index ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true
end
add_foreign_key "pockets", "users"
And Pocket model:
class Pocket < ApplicationRecord
belongs_to :user
end
This is actually the first time that happened to me and I have no idea which attribute should I rename in order to avoid conflicts?
You are using the transaction column in your Pocket model
t.integer "transaction"
I would suggest against using a column which has a similar name to a rails method. The transaction method is defined in transcations.rb module of Rails
But, if you really want to use it, you can check safe_attributes gem.
In your pockets table or migration, transaction is a reserved word, so you cannot use that word.

Rails5 - Adding Foreign Keys to Existing Models in postgres

I have two models. An Events model and an EventOption. The Events will have_many :event_options.
My issue is that when I try to do a migration to add_foreign key :event_options, :events so that I can link them up, I get the following error:
ActiveRecord::StatementInvalid: PG::UndefinedColumn: ERROR: column "event_id" referenced in foreign key constraint does not exist
: ALTER TABLE "event_options" ADD CONSTRAINT "fk_rails_3995702fad"
FOREIGN KEY ("event_id")
REFERENCES "events" ("id")
Here's my schema.rb:
ActiveRecord::Schema.define(version: 20160806001743) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"
create_table "event_options", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.float "price"
t.text "description"
t.string "name"
end
create_table "events", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "name"
t.boolean "active", default: true
end
create_table "users", force: :cascade do |t|
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.string "email", null: false
t.string "encrypted_password", limit: 128, null: false
t.string "confirmation_token", limit: 128
t.string "remember_token", limit: 128, null: false
t.index ["email"], name: "index_users_on_email", using: :btree
t.index ["remember_token"], name: "index_users_on_remember_token", using: :btree
end
end
I know there are :id columns that work because I can play with them in the console. I know I'm missing something here to get the Foreign Keys working for the app, but for the life of me, I don't know what.
Wait, you mean the foreign key option for the has_many, that isn't what add_foreign_key does confusingly. It adds a foreign key constraint.
So in your migration you need to do add_column or add_reference first
add_reference :event_options, :event, index: true
add_foreign_key :event_options, :events

Deleting table from schema - Rails

I want to delete a table in my schema. I created the database when I first started the project and want the table removed. What is the best way of doing this?
I tried rails g migration drop table :installs but that just creates a empty migration?
Schema:
create_table "installs", 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.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "installs", ["email"], name: "index_installs_on_email", unique: true
add_index "installs", ["reset_password_token"], name: "index_installs_on_reset_password_token", unique: true
If you create an empty migration by running:
rails g migration DropInstalls
or:
rails generate migration DropInstalls
You can then add this into that empty migration:
class DropInstalls < ActiveRecord::Migration
def change
drop_table :installs
end
end
Then run rake db:migrate in the command line which should remove the Installs table
Note: db/schema.rb is an autogenerated file. To alter the db structure, you should use the migrations instead of attempting to edit the schema file.

PG::Error: ERROR: null value in column "uid" violates not-null constraint

So, I setup a staging, and develop branch and now I'm getting this error when registering a new user locally:
PG::Error: ERROR: null value in column "uid" violates
not-null constraint DETAIL: Failing row contains
(7, test#gmail.com, $2a$10$WfiHKzYq4ovZtZoVAB.fRertuOSWezZYBw.RxkSqfBK89WbzcYxDK,
null, null, null, 0, null, null, null, null,
2013-08-13 07:20:13.484617, 2013-08-13 07:20:13.484617,
null, null, null, null). : INSERT INTO "users"
("created_at", "email", "encrypted_password", "updated_at")
VALUES ($1, $2, $3, $4) RETURNING "uid"
schema.rb snippit
create_table "users", id: false, force: true do |t|
t.integer "id", null: false
t.string "email", default: ""
t.string "encrypted_password", default: ""
t.string "reset_password_token"
t.datetime "reset_password_sent_at"
t.datetime "remember_created_at"
t.integer "sign_in_count", default: 0
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.datetime "created_at"
t.datetime "updated_at"
t.boolean "admin"
t.string "provider"
t.string "uid", null: false
t.string "username"
end
add_index "users", ["reset_password_token"], name: "index_users_on_reset_password_token", unique: true, using: :btree
I can't push any new code to production until this is worked out, which is annoying :/
Your uid is not-nullable but you aren't giving any values to it. Have your application generate one while you are registering users.
Ruby has a module named Secure Random which can be used to generate uids. It's usage is also pretty easy. Like,
require 'securerandom'
uid = SecureRandom.hex(10)
However, you can use factory girl to initialize your objects. For testing purpose though.
The problem is that your code to create a new user in the database is not defining a uid value for the row. Your database has constrained that field to disable rows from ever having uid as null. Therefore, you should either remove this constraint from the database or change your code to pass through a uid.

Resources