Rails Migrations failed to specify Foreign Key in MySQL Table - ruby-on-rails

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.

Related

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

Rails Search returns duplicate

I am doing an inner join (at least I think that's what the code is doing) but my search is returning the same result multiple times. I think I have something wrong with my join.
Tags
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | YES | | NULL | |
| color | varchar(255) | YES | | NULL | |
| article_id | int(11) | YES | MUL | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
Articles
+------------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| title | varchar(255) | YES | | NULL | |
| info | text | YES | | NULL | |
| created_at | datetime | NO | | NULL | |
| updated_at | datetime | NO | | NULL | |
+------------+--------------+------+-----+---------+----------------+
Each article I have tagged and it returns multiple results based on the tags. So if the article has 3 tags this results in 3 records being returned. Even though each article should only have 1 returned?
class Article < ApplicationRecord
has_many :tags, dependent: :destroy
validates :title, presence: true
def self.search(search)
if search
joins(:tags).where('title LIKE :search OR tags.name LIKE :search', search: "%#{search}%")
else
all
end
end
end
Use .distinct or .group. There is .uniq alias too starting from rails 4.0.2.
Example:
joins(:tags).where('title LIKE :search OR tags.name LIKE :search', search: "%#{search}%").distinct
joins(:tags).where('title LIKE :search OR tags.name LIKE :search', search: "%#{search}%").
group('article_id')

Rails migration won't create NOT NULL constraints

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

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.

OOP programming method question

I have 2 objects, Area and SurfBreak. Area has many SurfBreaks and a SurfBreak publishes its conditions based on wind,wave,tide info from Area. This bit I've done an it works well:-)
I now have list of forecast data for Area - future changes to Area's attributes.
Whats the best OOP method to show the Surfbreaks conditions using forecast data for Area ?
Many thanks
Andy
----Updated---
Its a rails app
class Spot < ActiveRecord::Base
belongs_to :area
has_many :forecasts, :through => :area
def has_swell
wind = "#{area.swelldir}"
beachstart = "#{breakstr}"
beachend = "#{breakend}"
if ( ((wind.to_i) + 360 - (beachstart.to_i)) % 360 <= ((beachend.to_i) + 360 - (beachstart.to_i)) % 360 )
"#{area.swelldir} Has Incoming swell "
else
"#{area.swelldir} No Swell"
end
end
class Area < ActiveRecord::Base
has_many :spots
has_many :forecasts
class Forecast < ActiveRecord::Base
belongs_to :area
The DB tables are the objects in rails. I've got Area and Spot working nicely but I now want to display forecasts for a spot. This is the bit I'm not sure about.
mysql> desc areas;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| descrip | varchar(255) | YES | | NULL | |
| winddir | int(11) | NO | | NULL | |
| windspd | int(11) | NO | | NULL | |
| swelldir | int(11) | NO | | NULL | |
| swellhgt | float | NO | | NULL | |
| tide | int(11) | NO | | NULL | |
| lat | float | YES | | NULL | |
| lng | float | YES | | NULL | |
+----------+--------------+------+-----+---------+----------------+
10 rows in set (0.00 sec)
mysql> desc spots;
+----------+--------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+----------+--------------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| name | varchar(255) | NO | | NULL | |
| descrip | varchar(255) | NO | | NULL | |
| breakstr | int(11) | NO | | NULL | |
| breakend | int(11) | NO | | NULL | |
| offstr | int(11) | NO | | NULL | |
| offend | int(11) | NO | | NULL | |
| besttide | int(11) | NO | | NULL | |
| area_id | int(11) | NO | | NULL | |
+----------+--------------+------+-----+---------+----------------+
9 rows in set (0.00 sec)
mysql> desc forecasts;
+--------------+----------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+--------------+----------+------+-----+---------+----------------+
| id | int(11) | NO | PRI | NULL | auto_increment |
| forecastdate | datetime | YES | | NULL | |
| area_id | int(11) | NO | | NULL | |
| winddir | int(11) | NO | | NULL | |
| windspd | int(11) | NO | | NULL | |
| swelldir | int(11) | NO | | NULL | |
| swellhgt | float | NO | | NULL | |
| tide | int(11) | NO | | NULL | |
+--------------+----------+------+-----+---------+----------------+
8 rows in set (0.00 sec)
So say an Area has 24 Forecast rows in a DB , one for every hour in the future. In my app
what is the best way to output a spots forecast conditions. Without changing the relevant values in the Area as Areas hold the current conditions. I could just pull all the forecast data into an array an loop through it changing the Area object data, but this doesn't seem very OOP to me ?
As output I'm after something like
Current Spot Details (Using spot methods on Area attributes)
xxx
Forecast Details for this spot (Using spot methods on Forecast attributes )
Hour 1 xxx
Hour 2 xxx
Hour 3 xxx
..
Sorry if this is not very well explained.
Regards
Andy
Your class Area sounds like it is doing too many things, and it is changing for different reasons. Separate it out so the Area has a list of WeatherData or something, so your forecasting code can iterate through the WeatherData without Area having to change. Your WeatherData object can include a flag saying whether it's real data or a forecast.
Class Area{
Wind wind;
Wave wave;
Tide tide;
}
Class SurfBreak extends Area{
//some SurfBreaks' field
public ForecastDetail getForecastDetail(){
//operate directly onwind wave tide fields and calculate
}
}
You haven't explained exactly how you developed first part of the problem (relations between Area and SurfBreaks), but, I would consider using of Observer design pattern here. So SurfBreaks would be Observers of Area changes.

Resources