rails foreign key violation error key not present in table - ruby-on-rails

I have two models division definition and model definition
division definition :
has_many :model_definitions, inverse_of: :division_definition, foreign_key: :division_definition_id, primary_key: :division_definition_id
ModelDefinition:
belongs_to :division_definition, inverse_of: :model_definitions, foreign_key: :division_id, primary_key: :division_id
In Db structure
def change
create_table :model_definitions do |t|
t.string :model_id , null: false, unique: true
t.string :car_model_name , null: false
t.belongs_to :division_definition , null: false
t.timestamps null: false
end
add_index :model_definitions, :division_definition_id
add_foreign_key :model_definitions, :division_definitions, on_delete: :restrict
end
divisiondefinition table
def change
create_table :division_definitions do |t|
t.integer :division_definition_id, null: false
t.string :division_name,null: false
t.timestamps null: false
end
add_index :division_definitions, :division_definition_id, unique: true
end
When i try to create model definition through division definition in console i'm getting following error
PG::ForeignKeyViolation: ERROR: insert or update on table "model_definitions" violates foreign key constraint "fk_rails_addc8b742e"
DETAIL: Key (division_definition_id)=(124) is not present in table "division_definitions".
: INSERT INTO "model_definitions" ("model_id", "car_model_name", "division_definition_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
(0.3ms) ROLLBACK
ActiveRecord::InvalidForeignKey: PG::ForeignKeyViolation: ERROR: insert or update on table "model_definitions" violates foreign key constraint "fk_rails_addc8b742e"
DETAIL: Key (division_definition_id)=(124) is not present in table "division_definitions".
: INSERT INTO "model_definitions" ("model_id", "car_model_name", "division_definition_id", "created_at", "updated_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id"
from /home/love/.rvm/gems/ruby-2.2.0/gems/activerecord-4.2.0/lib/active_record/connection_adapters/postgresql_adapter.rb:602:in `exec_prepared'
This is what i tried,
DivisionDefinition.create(division_definition_id: '124', division_name: 'BMW')
a = DivisionDefinition.last.model_definitions.build(model_id: 'bmw1', car_model_name: 'bmw1')
a = DivisionDefinition.last.model_definitions.build(model_id: 'bmw1', car_model_name: 'bmw1')
a.save
If i remove add_foreign_key from db i am able to create model definition through divisiondefinition
what is the error i'm making in migration file?

Your ModelDefinitions class refers to :division_id which appears nowhere else in your code.

Related

Error when creating association with polymorphic nested attribute

I have three models. Customer, Address, and CreditMemo.
Originally, I had a non polymorphic model Address and I changed it to polymorphic when I created CreditMemo so they could both share the same model. Since then, when I try to create a record with a different parent type than Customer, I get a validation error saying the Customer parent doesn't exist.
Before I created CreditMemo I simply had a 1-to-many association between Customer and Address. Here is the migration I made to change Address to polymorphic.
class MakeAddressPolymorphic < ActiveRecord::Migration[5.1]
def up
rename_column :addresses, :customer_id, :addressable_id
add_column :addresses, :addressable_type, :string
add_index :addresses, [:addressable_id, :addressable_type]
Address.reset_column_information
Address.update_all(:addressable_type => "Customer")
end
def down
rename_column :addresses, :addressable_id, :customer_id
remove_column :addresses, :addressable_type
end
end
My schema after the migration:
create_table "addresses", force: :cascade do |t|
t.text "line_1"
t.text "line_2"
t.string "city"
t.string "state"
t.string "zip_code"
t.string "address_type"
t.bigint "addressable_id"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.boolean "same_as_billing"
t.string "addressable_type"
t.index ["addressable_id", "addressable_type"], name: "index_addresses_on_addressable_id_and_addressable_type"
t.index ["addressable_id"], name: "index_addresses_on_addressable_id"
end
I can create a customer with the nested attributes just fine. However, when I try to create a CreditMemo with the nested address atrributes, I get this error:
PG::ForeignKeyViolation: ERROR: insert or update on table "addresses" violates foreign key constraint "fk_rails_d5f9efddd3" DETAIL: Key (addressable_id)=(36) is not present in table "customers". : INSERT INTO "addresses" ("line_1", "line_2", "city", "state", "zip_code", "address_type", "addressable_id", "created_at", "updated_at", "addressable_type") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) RETURNING "id"
Here is my Customer model:
has_many :addresses, :as => :addressable, inverse_of: :addressable
accepts_nested_attributes_for :addresses
My CreditMemo model:
has_many :addresses, :as => :addressable, inverse_of: :addressable
accepts_nested_attributes_for :addresses
My Address model:
belongs_to :addressable, :polymorphic => true
I'm pretty sure the issue is in this line which is at the bottom of my schema:
add_foreign_key "addresses", "customers", column: "addressable_id"
I'm not sure how to fix this or what it should be .
You can't use foreign keys with polymorphic associations, so you'd have to remove the foreign key from your schema, you can use remove_foreign_key for this.
Just write a new migration with this:
remove_foreign_key :addresses, :customers

Rails 5.2 ActiveStorage with UUIDs on Postgresql

We have our app using uuids are primary keys, on a Postgresql Database. (Standard setup described here).
We integrated ActiveStorage following the process described here. A standard setup using rails active_storage:install and migrated using rails db:migrate.
We have a model & corresponding controller as follows:
# Model
class Message < ApplicationRecord
has_one_attached :image
def filename
image&.attachment&.blob&.filename
end
end
# Controller
class MessagesController < ApplicationController
def create
message = Message.create!(message_params)
redirect_to message
end
private
def message_params
params.require(:message).permit(:title, :content, :image)
end
end
We observed that the first few sets of image were correctly associated with the model instances, but then we used to get random images for model instance, or got no image at all. Every time, we restart the server, we get first few images right, but then it was unpredictable.
Unsure, of what's going wrong, we debugged in rails console:
params[:image]
=> #<ActionDispatch::Http::UploadedFile:0x007fcf2fa97b70 #tempfile=#<Tempfile:/var/folders/dt/05ncjr6s52ggc4bk6fs521qw0000gn/T/RackMultipart20180726-8503-vg36kz.pdf>, #original_filename="sample.pdf", #content_type="application/pdf", #headers="Content-Disposition: form-data; name=\"file\"; filename=\"sample.pdf\"\r\nContent-Type: application/pdf\r\n">
On saving the instance and retrieving file name we got a random file, we uploaded previously.
#message = Message.new(message_params)
#message.filename
=> #<ActiveStorage::Filename:0x007fcf32cfd9e8 #filename="sample.pdf">
#message.save
#message.filename
=> #<ActiveStorage::Filename:0x007f82f2ad4ef0 #filename="OtherSamplePdf.pdf">
Looking for an explanation for this weird behaviour, and a possible solution too.
After hours of going line by line in the activestorage source code, and running the same commands
#message = Message.new(message_params)
#message.save
again and again. We got same random results again and again. Then we went through the logs rails printed while attaching the image to message and observed the following:
S3 Storage (363.4ms) Uploaded file to key: KBKeHJARTjnsVjkgSbbii4Bz (checksum: S0GjR1EyvYYbMKh44wqlag==)
ActiveStorage::Blob Create (0.4ms) INSERT INTO "active_storage_blobs" ("key", "filename", "content_type", "metadata", "byte_size", "checksum", "created_at") VALUES ($1, $2, $3, $4, $5, $6, $7) RETURNING "id" [["key", "KBKeHJARTjnsVjkgSbbii4Bz"], ["filename", "sample.pdf"], ["content_type", "application/pdf"], ["metadata", "{\"identified\":true}"], ["byte_size", 3028], ["checksum", "S0GjR1EyvYYbMKh44wqlag=="], ["created_at", "2018-07-26 04:54:33.029769"]]
ActiveStorage::Attachment Create (2.7ms) INSERT INTO "active_storage_attachments" ("name", "record_type", "record_id", "blob_id", "created_at") VALUES ($1, $2, $3, $4, $5) RETURNING "id" [["name", "file"], ["record_type", "Message"], ["record_id", "534736"], ["blob_id", "0"], ["created_at", "2018-07-26 05:04:35.958831"]]
record_id was being set as 534736, instead of an uuid. Here's where we went wrong.
Active storage was expecting integer foreign key to our Message model, and we wanted it to use uuids instead. So we had to fix our migration, to use uuids instead of integer foreign keys.
Solution:
class CreateActiveStorageTables < ActiveRecord::Migration[5.2]
def change
create_table :active_storage_blobs, id: :uuid do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments, id: :uuid do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
t.references :blob, null: false, type: :uuid
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
end
end
end
Hope this helps, someone facing similar issues. cheers!
I'm late to the party hitting this in 2020 but as anurag mentioned this is due to the active_storage_attachments DB table using a bigint for the record_id. I wasn't able to migrate all the models with ActiveStorage attachments to use UUIDs so I needed a way to support both UUIDs and bigints at the same time.
Warning: If you can avoid this (by migrating everything to UUID most likely) then I'd strongly recommend doing that, and I plan on doing that as soon as we have time.
Warnings aside, migrating the active_storage_attachments table to change the record_id column to be text does work. I had to adjust the few places in our app where we were joining against the active_storage_attachments table using record_id to cast the value to text in the join. For example I used the following code when joining to a model that had a UUID ID.
.joins("
LEFT OUTER JOIN active_storage_attachments
ON active_storage_attachments.record_id = documents.id::text
")
Hopefully this helps someone else who is stuck in the halfway state where not all ActiveStorage using models are using UUIDs or bigint IDs.
I also had this issue. My models all use UUIDs. As I had no records in ActiveStorage needing to be retained, I dropped and recreated the :active_storage_attachments and :active_storage_blobs tables. Here is my migration, in case it's of use to anyone. Using Rails 6.0.4.
def change
reversible do |dir|
dir.up do
drop_table :active_storage_attachments
drop_table :active_storage_blobs
create_table "active_storage_blobs", id: :uuid,
default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table "active_storage_attachments", id: :uuid,
default: -> { "gen_random_uuid()" }, force: :cascade do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false, type: :uuid
t.references :blob, null: false, type: :uuid
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
dir.down do
drop_table :active_storage_attachments
drop_table :active_storage_blobs
# original tables generated by rails
create_table :active_storage_blobs do |t|
t.string :key, null: false
t.string :filename, null: false
t.string :content_type
t.text :metadata
t.bigint :byte_size, null: false
t.string :checksum, null: false
t.datetime :created_at, null: false
t.index [ :key ], unique: true
end
create_table :active_storage_attachments do |t|
t.string :name, null: false
t.references :record, null: false, polymorphic: true, index: false
t.references :blob, null: false
t.datetime :created_at, null: false
t.index [ :record_type, :record_id, :name, :blob_id ], name: "index_active_storage_attachments_uniqueness", unique: true
t.foreign_key :active_storage_blobs, column: :blob_id
end
end
end
end

why PG::UniqueViolation: ERROR: duplicate key value violates unique constraint?

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

Rails STI and has_many through association not working, SQLException: no such table

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 =)

Error while saving floats to PostgreSQL from Rails

I just recently made the switch over to PostgreSQL.. and just wondering about this issue:
PG::Error: ERROR: column "bathrooms" is of type double precision but expression is of type character varying at character 161
HINT: You will need to rewrite or cast the expression.
: INSERT INTO "properties" ("address", "bathrooms", "bedrooms", "city", "country", "created_at", "description", "name", "state", "updated_at", "zip") VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) RETURNING "id"
Migration looks like this:
class CreateProperties < ActiveRecord::Migration
def change
create_table :properties do |t|
t.string :name
t.string :address
t.string :city
t.string :state
t.string :zip
t.string :country
t.float :bedrooms
t.float :bathrooms
t.string :country
t.text :description
t.timestamps
end
end
end
I switched the float to decimal, it seems PostgreSQL is fine with that implicit cast now?
Either way the error is gone, thanks

Resources