Rails migration won't create NOT NULL constraints - ruby-on-rails

I have this db migration for my Rails 3.2 plugin:
class CreateIssueChangeApprovements < ActiveRecord::Migration
def change
create_table :issue_change_approvements do |t|
t.timestamps
t.references :issue, :null => false
t.references :user, :null => false
t.string :field_type, :limit => 30
t.string :old_value
t.string :value
t.integer :approved_by_id
t.boolean :is_closed, :default => 0
t.string :status, :default => '', :limit => 30
end
add_index :issue_change_approvements, :issue_id
add_index :issue_change_approvements, :user_id
end
end
Once I run it against a MySQL database, I see no NOT NULL constraints on issue_id and user_id fields as well as no foreign keys.
Can anyone tell me what is the right way to do this?
UPD:
The weird thing here is that when I go to MySQL Workbench and generate a script for my newly created table, i get this:
CREATE TABLE `issue_change_approvements` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`created_at` datetime NOT NULL,
`updated_at` datetime NOT NULL,
`issue_id` int(11) NOT NULL,
`user_id` int(11) NOT NULL,
`field_type` varchar(30) DEFAULT NULL,
`old_value` varchar(255) DEFAULT NULL,
`value` varchar(255) DEFAULT NULL,
`approved_by_id` int(11) DEFAULT NULL,
`is_closed` tinyint(1) DEFAULT '0',
`status` varchar(30) DEFAULT '',
PRIMARY KEY (`id`),
KEY `index_issue_change_approvements_on_issue_id` (`issue_id`),
KEY `index_issue_change_approvements_on_user_id` (`user_id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;
However when I run a statement like this:
insert into issue_change_approvements(value) values('hello')
it executes successfully and adds a row with lots of NULL values.
UPD 2:
This is what describe issue_change_approvements shows:
+----------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
| issue_id | int(11) | NO | MUL | NULL | |
| user_id | int(11) | NO | MUL | NULL | |
| field_type | varchar(30) | YES | | NULL | |
| old_value | varchar(255) | YES | | NULL | |
| value | varchar(255) | YES | | NULL | |
| approved_by_id | int(11) | YES | | NULL | |
| is_closed | tinyint(1) | YES | | 0 | |
| status | varchar(30) | YES | | | |
+----------------+--------------+------+-----+---------+----------------+

Rails does not create any constraints by default. The null: false option will just not allow null values for this sql field, but it will not add a constraint.
Instead of foreign_key constraints in rails you usually just set the desired option on model relationship, e.g has_many :issues, dependent: :destroy
I guess this strategy was chosen for faster schema migrations with less downtime.
However there is an option to use constraints on db level by explicitly specifying them. You can use the foreigner gem prior to rails 4.2 and with rails 4.2 the dsl for them is included by default.
See
Have Some (Referential) Integrity with Foreign Keys
2.5 Foreign Key Support

Related

Rails PG::NotNullViolation: ERROR: null value in column "id" of relation "sales" violates not-null constraint

Problem: When trying to save a record (Sale.create even), I get this error every time
PG::NotNullViolation: ERROR: null value in column "id" of relation "sales" violates not-null constraint
Work-around: If I drop and re-create my database it fixes the problem. After the work-around it will work for a while until it starts happening again.
My hypothesis: Somehow my test database seems to be getting into a state when it "loses" (🤷) it's auto-increment of the ID column for at least 1 table.
Confounding problem: This didn't happen with SQLite.
Background: We know that Rails creates the ID column with auto_increment and internally the database tracks that somewhere.
Question: How can I debug this further?
Versions:
Rails 6.0.3.4
Ruby 2.7.2
Postgres 13.1
Sale Class
class Sale < ApplicationRecord
belongs_to :horse_variant, optional: true
belongs_to :origin_location, optional: true
has_many :sale_adjustments, dependent: :delete_all
has_many :fulfillments, dependent: :delete_all
has_many :fulfillment_orders, dependent: :delete_all
has_many :refunds, dependent: :delete_all
enum fulfillment_status: {
null: 0,
partial: 1,
fulfilled: 2,
}
scope :unknown_variant, -> { where(horse_variant_id: HorseVariant.unknown_horse_variant_id) }
Postgres schema dump
Table "public.sales"
Column | Type | Collation | Nullable | Default | Storage | Stats target | Description
--------------------+--------------------------------+-----------+----------+-----------------------------------+----------+--------------+-------------
id | bigint | | not null | nextval('sales_id_seq'::regclass) | plain | |
horse_variant_id | bigint | | not null | | plain | |
origin_location_id | bigint | | not null | | plain | |
quantity | bigint | | not null | | plain | |
date | date | | not null | | plain | |
shopify_order_name | character varying | | not null | | extended | |
shopify_order_id | bigint | | not null | | plain | |
order_line_item_id | bigint | | not null | | plain | |
created_at | timestamp(6) without time zone | | not null | | plain | |
updated_at | timestamp(6) without time zone | | not null | | plain | |
discount | double precision | | | | plain | |
fulfillment_status | integer | | | 0 | plain | |
status | integer | | | 0 | plain | |
Indexes:
"sales_pkey" PRIMARY KEY, btree (id)
"index_sales_on_date" btree (date)
"index_sales_on_date_and_horse_variant_id" btree (date, horse_variant_id)
"index_sales_on_horse_variant_id" btree (horse_variant_id)
"index_sales_on_order_line_item_id" UNIQUE, btree (order_line_item_id)
"index_sales_on_origin_location_id" btree (origin_location_id)
"index_sales_on_shopify_order_id" btree (shopify_order_id)
Foreign-key constraints:
"fk_rails_6df22f6f3c" FOREIGN KEY (origin_location_id) REFERENCES origin_locations(id)
"fk_rails_cab8258b6a" FOREIGN KEY (horse_variant_id) REFERENCES horse_variants(id)
Referenced by:
TABLE "sale_adjustments" CONSTRAINT "fk_rails_19fd8872e1" FOREIGN KEY (sale_id) REFERENCES sales(id)
TABLE "refunds" CONSTRAINT "fk_rails_4e4e302c3a" FOREIGN KEY (sale_id) REFERENCES sales(id)
TABLE "fulfillment_orders" CONSTRAINT "fk_rails_8897bbd770" FOREIGN KEY (sale_id) REFERENCES sales(id)
TABLE "fulfillments" CONSTRAINT "fk_rails_e8f7460348" FOREIGN KEY (sale_id) REFERENCES sales(id)
Access method: heap
IRB (pry) session
[1] pry(main)> Sale.create
W, [2021-04-04T18:57:11.712216 #58350] WARN -- : Creating scope :test. Overwriting existing method Sale.test.
D, [2021-04-04T18:57:11.782069 #58350] DEBUG -- : (0.2ms) BEGIN
D, [2021-04-04T18:57:11.787486 #58350] DEBUG -- : Sale Create (5.1ms) INSERT INTO "sales" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2021-04-04 22:57:11 UTC"], ["updated_at", "2021-04-04 22:57:11 UTC"]]
D, [2021-04-04T18:57:11.787842 #58350] DEBUG -- : Query Trace:
bin/rails:9:in `<main>'
D, [2021-04-04T18:57:11.788096 #58350] DEBUG -- : (0.1ms) ROLLBACK
D, [2021-04-04T18:57:11.788316 #58350] DEBUG -- : Query Trace:
bin/rails:9:in `<main>'
ActiveRecord::NotNullViolation: PG::NotNullViolation: ERROR: null value in column "id" of relation "sales" violates not-null constraint
DETAIL: Failing row contains (null, null, null, null, null, null, null, null, 2021-04-04 22:57:11 UTC, 2021-04-04 22:57:11 UTC, null, 0, 0).
from /Users/marcbeaupre/.rvm/gems/ruby-2.7.2/gems/activerecord-6.0.3.4/lib/active_record/connection_adapters/postgresql_adapter.rb:675:in `exec_params'
Caused by PG::NotNullViolation: ERROR: null value in column "id" of relation "sales" violates not-null constraint
DETAIL: Failing row contains (null, null, null, null, null, null, null, null, 2021-04-04 22:57:11 UTC, 2021-04-04 22:57:11 UTC, null, 0, 0).
from /Users/marcbeaupre/.rvm/gems/ruby-2.7.2/gems/activerecord-6.0.3.4/lib/active_record/connection_adapters/postgresql_adapter.rb:675:in `exec_params'

List all associated model records present in another model present in another namespace in rails

I have two models like:
class Superadmin::Company < ApplicationRecord
belongs_to :user
has_many :garments
end
2nd
class Garment < ApplicationRecord
belongs_to :company ,:class_name => "Superadmin::Company"
end
But when I search like
company = Superadmin::Company.find(9)
company.garments
Its give error: as
Garment Load (1.3ms) SELECT `garments`.* FROM `garments` WHERE `garments`.`company_id` = 9 ORDER BY created_at asc
ActiveRecord::StatementInvalid: Mysql2::Error: Unknown column 'garments.company_id' in 'where clause': SELECT `garments`.* FROM `garments` WHERE `garments`.`company_id` = 9 ORDER BY created_at asc
from /home/tukatech/rails_projects/live_tukagarments/.bundle/gems/activerecord-5.0.7.1/lib/active_record/connection_adapters/abstract_mysql_adapter.rb:218:in `query'
Table names in database is as:
1. garments
2. superadmin_companies
please provide if there is a correct way to search using rails foreign key associations relation.
Data base is as:
mysql> desc superadmin_companies;
+-------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| address | varchar(255) | YES | | NULL | |
| phone | varchar(255) | YES | | NULL | |
| user_id | int(11) | YES | MUL | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
| logo_file_name | varchar(255) | YES | | NULL | |
| logo_content_type | varchar(255) | YES | | NULL | |
| logo_file_size | int(11) | YES | | NULL | |
| logo_updated_at | datetime | YES | | NULL | |
+-------------------+--------------+------+-----+---------+----------------+
11 rows in set (0.00 sec)
mysql> desc garments;
+--------------------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| xhtml_file_file_name | varchar(255) | YES | | NULL | |
| xhtml_file_content_type | varchar(255) | YES | | NULL | |
| xhtml_file_file_size | int(11) | YES | | NULL | |
| xhtml_file_updated_at | datetime | YES | | NULL | |
| xhtml_thumb_file_name | varchar(255) | YES | | NULL | |
| xhtml_thumb_content_type | varchar(255) | YES | | NULL | |
| xhtml_thumb_file_size | int(11) | YES | | NULL | |
| xhtml_thumb_updated_at | datetime | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
| category | varchar(255) | YES | | NULL | |
| garment_type | varchar(255) | YES | | NULL | |
| user_id | int(11) | YES | | NULL | |
| superadmin_company_id | int(11) | YES | MUL | NULL | |
+--------------------------+--------------+------+-----+---------+----------------+
15 rows in set (0.00 sec)
As per the description mentioned in the post and the comments in one of the answers it seems like the relation defined in the models is unable to relate with the column names.
For it to work, please change to the one below:
class Superadmin::Company < ApplicationRecord
belongs_to :user
has_many :garments, class_name: "Garment", foreign_key: "superadmin_company_id"
end
Now it will start mapping the with the foreign_key specified in the relationship.
Update association as below:
class Superadmin::Company < ApplicationRecord
has_many :garments, foreign_key: 'superadmin_company_id'
end
class Garment < ApplicationRecord
belongs_to :company, class_name: 'Superadmin::Company', foreign_key: 'superadmin_company_id'
end
There is no company_id column in the garments table. You have to add it via migration. Try:
rails generate migration AddCompanyToGarment company:references

Can't Unlink Records in Tables from Ruby Console

Working my way through Lynda.com's course on Rails 5 and I ran into a glitch. Don't know if the issue is my setup, a difference in revs (I believe the instructor is using v5.0.0, while I'm using v5.1.4), I'm doing it wrong, or there's a bug in rails console.
The lesson is Chapter 6 Associations, section 2 One-to-One Associations. The lesson uses two tables: subjects and pages. Their models are:
class Subject < ApplicationRecord
has_one :page
# and a bunch of scopes from an earlier lesson
end
And:
class Page < ApplicationRecord
belongs_to :subject
end
Using MySQL v14.14 Distrib 5.7.19 for osx10.12.
mysql> SHOW FIELDS FROM subjects;
+------------+-------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+-------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| name | varchar(50) | YES | | NULL | |
| position | int(11) | YES | | NULL | |
| visible | tinyint(1) | YES | | 0 | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+-------------+------+-----+---------+----------------+
And:
mysql> SHOW FIELDS FROM pages;
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| subject_id | int(11) | YES | MUL | NULL | |
| name | varchar(50) | YES | | NULL | |
| permalink | varchar(100) | YES | MUL | NULL | |
| position | int(11) | YES | | NULL | |
| visible | tinyint(1) | YES | | 0 | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
The field to watch here is pages.subject_id. Note that it can be nulled.
From rails console I'd created a series of records in subjects, in a previous lesson. In this lesson I create a record in pages and link it with subject record. Great! Works perfectly. Then I try to unlink it just as the instructor does, BUT IT FAILS FOR ME.
At the Rails command line I retrieve a couple of objects from the database:
iirb(main):022:0* subject2 = Subject.find_by_name("Next Subject")
Subject Load (0.6ms) SELECT `subjects`.* FROM `subjects` WHERE `subjects`.`name` = 'Next Subject' LIMIT 1
=> #<Subject id: 2, name: "Next Subject", position: 2, visible: true, created_at: "2017-09-21 21:17:25", updated_at: "2017-09-21 21:51:39">
irb(main):023:0> page2 = Page.find_by_name("Next Page")
Page Load (3.3ms) SELECT `pages`.* FROM `pages` WHERE `pages`.`name` = 'Next Page' LIMIT 1
=> #<Page id: 6, subject_id: nil, name: "Next Page", permalink: "next", position: 2, visible: false, created_at: "2017-09-24 19:00:52", updated_at: "2017-09-24 19:53:38">
Note that pages.subject_id is nil. The records are unlinked.
From the Rails command line both page2.subject and subject2.page return nil, as expected. I can link them with:
irb(main):029:0> subject2.page = page2
(2.7ms) BEGIN
SQL (9.2ms) UPDATE `pages` SET `subject_id` = 2, `updated_at` = '2017-09-25 14:40:09' WHERE `pages`.`id` = 6
(3.0ms) COMMIT
=> #<Page id: 6, subject_id: 2, name: "Next Page", permalink: "next", position: 2, visible: false, created_at: "2017-09-24 19:00:52", updated_at: "2017-09-25 14:40:09">
Now page2.subject and subject2.page works, as expected.
AND THEN THE PROBLEM. The instructor says I can unlink the records with subject2.page = nil, but:
irb(main):037:0* subject2.page = nil
(0.3ms) BEGIN
(0.2ms) ROLLBACK
ActiveRecord::RecordNotSaved: Failed to remove the existing associated page. The record failed to save after its foreign key was set to nil.
from (irb):37
It claims the record failed to save. Could it be the value of nil (or null) in pages.subject_id?
The page2 object shows that subject_id has been reset to its original value, not nil. If I try to set this to nil and then save page2 to the database, it fails.
But if I reload the page2 object in memory from the database, manually set subject_id to nil, and then save page2, it works! At least the pages database shows correct contents.
However if I check page2.subject and subject2.page the Rails command line shows they're still linked. I need to reload these objects in memory from the database before what's in memory shows the correct result.
It seems subject2.page = nil (which works for the instructor) unlinks the records in both the database and memory--at least that's how it's supposed to work. Manually unlinking works in the database, but memory needs to be updated from the database for full functionality.
So what's going on?
Thanks a million for your help.
PS: Mac OS-X v10.12.6 (Sierra), Ruby v2.4.1p111, Rails v5.4.1 (both installed via Homebrew), using VS Code v1.16.1 as my editor, and project setup through the command line (showing exactly the same files and directories as instructor has)
Added info: relevant part of schema.rb
ActiveRecord::Schema.define(version: 20170920222914) do
create_table "pages", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.integer "subject_id"
t.string "name", limit: 50
t.string "permalink", limit: 100
t.integer "position"
t.boolean "visible", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
t.index ["permalink"], name: "index_pages_on_permalink"
t.index ["subject_id"], name: "index_pages_on_subject_id"
end
create_table "subjects", force: :cascade, options: "ENGINE=InnoDB DEFAULT CHARSET=utf8" do |t|
t.string "name", limit: 50
t.integer "position"
t.boolean "visible", default: false
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
end
Moving comment down with a bit more detail.
In Rails 5.0 and up they've changed how the default belongs_to associations work. From the upgrade guides it states:
belongs_to will now trigger a validation error by default if the association is not present.
This can be turned off per-association with optional: true.
In your question you show your pages table (thanks for the details by the way) and it indicates that your foreign key can be null. But when you go to save, rails does it's own default validation and rollsback your save since you're using rails 5.1.4.
I believe this is now a default validation as it's helping enforce foreign key constraints. Typically you'd delete the subject and cascade delete the page, or delete the page but keep the subject. But there are times you want to do what you're requesting depending on your structure.
I'm assuming the tutorial your following is using rails 4.2.
It sounds like there's a foreign key constraint in place in your database maybe? Can you update your original post and provide your entire db/schema.rb file please?
If you do not want the Page record to be deleted when you nullify the foreign key, you should specify dependent: :nullify on your has_one association. That may fix your issue too.
class Subject < ApplicationRecord
has_one :page, dependent: :nullify
# and a bunch of scopes from an earlier lesson
end

Running remove_column not working in rails migration

I have a very simple migration:
class RemoveAuthorIdFromBooks < ActiveRecord::Migration
def change
remove_column :books, :author_id
end
end
But I get the following error:
Mysql2::Error: Error on rename of './mysite_staging/#sql-3b1_3c78' to './mysite_staging/books' (errno: 150): ALTER TABLE `books` DROP `author_id`
This is the description of the table:
+------------------+---------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------------+---------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| author_id | int(11) | NO | MUL | NULL | |
| title | varchar(255) | NO | | NULL | |
| teaser | varchar(500) | NO | | NULL | |
| description | varchar(2000) | YES | | NULL | |
| cover_image | varchar(255) | NO | | NULL | |
| publication_date | date | NO | | NULL | |
| enabled | tinyint(1) | NO | | 1 | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
| excerpt | text | YES | | NULL | |
| featured | tinyint(1) | YES | | NULL | |
| site_id | int(11) | YES | | NULL | |
+------------------+---------------+------+-----+---------+----------------+
Any clues?
If anyone still facing this with Rails 4 and above, then you could do the following
remove_reference(:books, :author, index: true, foreign_key: true)
For some reason a foreign key constraint was breaking the drop sentence.
I did the following:
show create table books;
Looked at the foreign key name and then:
alter table books drop foreign key books_ibfk_1;
Then rake db:migrate worked.

Rails Migrations failed to specify Foreign Key in MySQL Table

class CreateTestings < ActiveRecord::Migration
def self.up
create_table :testings do |t|
t.string "name"
t.boolean "visible"
t.string "description"
t.integer "roll"
t.references "students"
t.timestamps
end
end
def self.down
drop_table :testings
end
end
Hello, i just ran this test migration to see how Rails handles Migrations. Even though i have
t.references "students"
Rails created the students_id in my testings table successfully but however didn't specified any foreign key on it:
mysql> DESC testings;
+-------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+-------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| visible | tinyint(1) | YES | | NULL | |
| description | varchar(255) | YES | | NULL | |
| roll | int(11) | YES | | NULL | |
| students_id | int(11) | YES | | NULL | |
| created_at | datetime | YES | | NULL | |
| updated_at | datetime | YES | | NULL | |
+-------------+--------------+------+-----+---------+----------------+
Is this how Rails works or otherwise i should've had
t.references :student instead of t.references "students"
Thanks!
This is how rails works. It doesn't specify foreign key dependencies in its migrations. You'll have to do this manually with an 'execute' command and SQL code if you do want to specify foreign-key dependencies.

Resources