I have two models in use at this point: Airports and Flights.
Flights belong to both an origin airport and destination airport. When I try to create a Flight model instance, I get the following error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such table: main.destination_airports
I have set up my code to match the example here: https://www.theodinproject.com/courses/ruby-on-rails/lessons/active-record-associations
I've researched the problem (here and elsewhere), but can't turn up anything that seems to match my situation. I can't figure out what's going wrong. Has to be something simple. Something with the migrations, maybe? Thanks in advance for the help.
Relevant code:
Airport model:
class Airport < ApplicationRecord
has_many :departing_flights, foreign_key: "origin_airport_id",
class_name: "Flight"
has_many :arriving_flights, foreign_key: "destination_airport_id",
class_name: "Flight"
validates :abbreviation, presence: true, length: { is: 3 }
end
Flight model:
class Flight < ApplicationRecord
belongs_to :origin_airport, class_name: "Airport"
belongs_to :destination_airport, class_name: "Airport"
has_many :bookings
has_many :passengers, through: :bookings
end
Migrations:
class CreateAirports < ActiveRecord::Migration[5.1]
def change
create_table :airports do |t|
t.string :abbreviation
t.string :full_name
t.string :city
t.string :state
t.string :zip
t.timestamps
end
end
end
class CreateFlights < ActiveRecord::Migration[5.1]
def change
create_table :flights do |t|
t.references :origin_airport, foreign_key: true
t.references :destination_airport, foreign_key: true
t.datetime :depart_time
t.datetime :arrive_time
t.integer :capacity
t.string :airline
t.string :flight_number
t.timestamps
end
end
end
Check the filenames of the migrations, perhaps they are not being run in the correct order?
Also, you may just need to run the migration in the right environment (RAILS_ENV=development)
Update: I found the (an?) answer - changing the migration for the flights table to replace t.references with t.integer and the column name with "x_id" corrects the problem. So, does references only work if using the model name for the column id?
Related
I have 2 Models with association has_many along with cascade property between them.
class ServicesBrandDetail < ApplicationRecord
has_many :services_brands, foreign_key: "brand_id", dependent: :delete_all
end
class ServicesBrand < ApplicationRecord
belongs_to :services_brand_details, foreign_key: "brand_id",
end
Migration for both files
class CreateServicesBrandDetails < ActiveRecord::Migration[6.1]
def change
create_table :services_brand_details do |t|
t.string :brand
t.string :mail_list
t.string :cc_list
t.timestamps
end
end
end
class CreateServicesBrands < ActiveRecord::Migration[6.1]
def change
create_table :services_brands do |t|
t.string :warehouse
t.references :brand, null: false, foreign_key: {to_table: :services_brand_details}
t.timestamps
end
end
end
Now I was able to create and save data in from ServicesBrandDetails model. but the Problem is when i create record from ServiceBrand It created record perfectly but i was not able to store data in DB.
record = ServicesBrandDetail.create(:brand => "a", :mail_list => 'abc#mail.com', :cc_list => 'def#mail.com')
record.save
Record successfully stored in DB.
child = record.services_brands.new(:warehouse => "in") <-- record was created successfully.
child.save
it give me error
C:/Ruby30-x64/lib/ruby/gems/3.0.0/gems/activerecord-6.1.5/lib/active_record/inheritance.rb:237:in `compute_type': uninitialized constant ServicesBrand::ServicesBrandDetails (NameError)
Please follow proper Naming convention
This article might help - https://www.bigbinary.com/learn-rubyonrails-book/summarizing-rails-naming-conventions
In ServiceBrand Model
class ServiceBrand < ApplicationRecord
belongs_to :brand, class_name: 'ServiceBrandDetail'
end
belongs_to should be foreign key name i.e brand in your case
You can delete existing models and tables from your codebase and try below one. (I've tested)
class ServiceBrandDetail < ApplicationRecord
has_many :service_brands, foreign_key: :brand_id, dependent: :delete_all
end
class ServiceBrand < ApplicationRecord
belongs_to :brand, class_name: 'ServiceBrandDetail'
end
Migration for both files
class CreateServiceBrandDetails < ActiveRecord::Migration[6.1]
def change
create_table :service_brand_details do |t|
t.string :brand
t.string :mail_list
t.string :cc_list
t.timestamps
end
end
end
class CreateServiceBrands < ActiveRecord::Migration[6.1]
def change
create_table :service_brands do |t|
t.string :warehouse
t.references :brand, null: false, foreign_key: {to_table: :service_brand_details}
t.timestamps
end
end
end
Then try to create model objects which you tried in your question. It will work 👍🏽
In your model ServicesBrand you have to use singular association name for belongs_to
Change this belongs_to :services_brand_details to this belongs_to :services_brand_detail
class ServicesBrand < ApplicationRecord
belongs_to :services_brand_detail, foreign_key: "brand_id"
end
I have a Player class that I want to have high_school_team and club_team properties. So then I figure Player will have high_school_team_id and club_team_id properties that point to the corresponding team. I try to do this in the following migration, but it doesn't work.
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.string :first_name
t.string :middle_name
t.string :last_name
t.decimal :height
t.decimal :weight
t.date :birthday
t.references :team, :high_school_team, foreign_key: true
t.references :team, :club_team, foreign_key: true
t.decimal :gpa
t.string :class_year
t.string :intended_major
t.string :email
t.string :phone_number
t.text :notes
t.timestamps
end
end
end
It gives the following error:
code/scout-db [master●] » rails db:migrate
== 20191218003854 CreatePlayers: migrating ====================================
-- create_table(:players)
rails aborted!
StandardError: An error has occurred, this and all later migrations canceled:
you can't define an already defined column 'team_id'.
/Library/Ruby/Gems/2.6.0/gems/activerecord-6.0.2.1/lib/active_record/connection_adapters/abstract/schema_definitions.rb:372:in `column'
...
HighSchoolTeam and ClubTeam are models that do single table inheritance with Team.
I don't see why I'm getting the error. The docs seem to say that the first argument for t.referenes is table_name and the second ref_name. :team is the name of the table and I want the references to be high_school_team_id and club_team_id.
When I switch the order of the arguments to t.references, it still doesn't work. Somehow it gives the same error: you can't define an already defined column 'team_id'..
It looks you you are confusing SchemaStatement#add_reference and TableDefinition#references which have completely different signatures.
If you want to setup a foreign key column where the table can't be derived from the name of the column (the first argument) you just pass foreign_key: { to_table: :teams}.
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
t.references :high_school_team, foreign_key: { to_table: :teams}
t.references :club_team, foreign_key: { to_table: :teams}
end
end
end
t.references :high_school_team, index: true as recommended by the other answers is NOT equivilent. That just adds a index to the column but no foreign key constraint.
You can then setup the assocations on Player as:
class Player < ApplicationRecord
belongs_to :high_school_team, class_name: 'Team'
belongs_to :club_team, class_name: 'Team'
end
You can't use a single has_many :players assocation on the other end though as the foreign key column can be either players.high_school_team_id or players.club_team_id.
class Team
has_many :high_school_team_players,
foreign_key: :high_school_team_id,
class_name: 'Player'
has_many :club_team_players,
foreign_key: :club_team_id,
class_name: 'Player'
end
But really a better alternative in the first place would have been to setup a join table:
class Player
has_many :placements
has_one :high_school_team,
through: :placements,
source: :team
class_name: 'Team'
has_one :club_team,
through: :placements,
source: :team
class_name: 'Team'
end
class Placement
belongs_to :player
belongs_to :team
end
class Team
has_many :placements
has_many :players, through: :placements
end
The doc you mentioned talks about the case when you need to add reference to an existing table.
For adding a refernence to a new table:
t.references :team, :high_school_team, foreign_key: true
This piece of code is wrong. Instead, it should be
t.references :high_school_team, foreign_key: {to_table: :teams}
to_table is needed to add database referential integrity
So your migration will be like this:
class CreatePlayers < ActiveRecord::Migration[6.0]
def change
create_table :players do |t|
....
t.references :high_school_team, foreign_key: {to_table: :teams}
t.references :club_team, foreign_key: {to_table: :teams}
....
end
end
end
I have a model named Letter and an other one named LetterTracking :
class Letter < ApplicationRecord
has_many :letter_trackings, as: :trackable
end
and:
class LetterTracking < ApplicationRecord
belongs_to :letter
has_many :letter_trackings, as: :trackable
end
this is my create table migration for Letter Tracking:
class CreateLetterTrackings < ActiveRecord::Migration[5.0]
def change
create_table :letter_trackings do |t|
t.integer :trackable_id, default: 0, null: false, unique: true
t.string :trackable_type
t.text :paraph
t.string :status
t.string :assignee
t.belongs_to :letter
t.timestamps
end
end
end
as you can see in below screen shots when I select a tracking record for the second tracking the relation is ok but when ever I add the third letter tracking the second one relation removes and the last one keeps the association.
What I want is to keep the letter tracking in each record not by the last one. I mean some thing like nested records in which I can keep the related records.
any Idea ?
Thank you
First of all as a second thought the polymorphic relation seems quite useless for keeping track in this case. The thing which fits best here is tree based relation I suppose.
this is my LetterTracking.rb
class LetterTracking < ApplicationRecord
belongs_to :letter
has_many :children, class_name: "LetterTracking", foreign_key: "parent_id"
belongs_to :parent, class_name: "LetterTracking"
end
and this is my letter.rb
class Letter < ApplicationRecord
has_many :letter_trackings
end
and finaly the LetterTrackings Migration:
class CreateLetterTrackings < ActiveRecord::Migration[5.0]
def change
create_table :letter_trackings do |t|
t.references :parent, index: true
t.text :paraph
t.string :status
t.string :assignee
t.belongs_to :letter, index: true
t.timestamps
end
end
end
Now I can have the records of the lettertrackings join together like a tree while keeping the letter id in every single record! Yep :)
I practice my RoR skills and try develop application to already created DB. It's have 4 tables: testplans, testplan_tcversions,* test_project* and nodes.
I'm code 2 models for this tables:
class TestPlan < ActiveRecord::Base
self.table_name= 'testplans'
belongs_to :test_project
has_many :test_suites, foreign_key: :testplan_id, inverse_of: :test_plan
has_one :node, foreign_key: :id, inverse_of: :test_plan
end
and
class TestSuite < ActiveRecord::Base
self.table_name='testplan_tcversions'
belongs_to :test_plan
has_one :node, foreign_key: id, inverse_of: :test_collection
end
But I get exception uninitialized constant TestPlan::TestSuite when try: #suits=TestPlan.find(4906).test_suites
I found a lot of answers that Models must singular and table must plural, but my Models names are singular, names of tables I point in self.table_name.
What I did wrong?
UPD
This my db:schema:dump
create_table "testplans", force: true do |t|
t.integer "testproject_id"
t.text "notes"
t.integer "active"
t.integer "is_open"
t.integer "is_public"
t.text "api_key"
end
create_table "testplan_tcversions", force: true do |t|
t.integer "testplan_id"
t.integer "tcversion_id"
t.integer "node_order"
t.integer "urgency"
t.integer "platform_id"
t.integer "author_id"
t.datetime "creation_ts"
end
How are your migrations set up?
If they are set up correctly, the relationship between TestSuite and TestPlan should look like this:
class TestPlan < ActiveRecord::Base
has_many :test_suites
end
class TestSuite < ActiveRecord::Base
belongs_to :test_plan
end
For this to work though, your TestSuite migration needs to have a test_plan_id column. That should look like this.
class TestSuite < ActiveRecord::Migration
belongs_to :test_plan
end
If this is set up correctly, you should then be able to call #suits=TestPlan.find(4906).test_suites.
Make sure your table names correspond to your model names. If you don't have a table named 'testplan_tcversions', the association isn't going to work.
I'm trying to create a many to many relationship between two models in Rails 3.2.11.
A User can be associated with many Incidents and vice versa.
class User < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :incident_participants, foreign_key: "participant_id"
has_many :participated_incidents, through: :incident_participants
end
class Incident < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
has_many :incident_participants, foreign_key: "participated_incident_id"
has_many :participants, through: :incident_participants
end
The join table:
class IncidentParticipant < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
t.belongs_to :participant, class_name: "User"
t.belongs_to :participated_incident, class_name: "Incident"
end
Table for IncidentParticipants
create_table "incident_participants", :force => true do |t|
t.integer "participant_id"
t.integer "participated_incident_id"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
So, why doesn't rails get this relationship? When I try to do #incident.participants in my view I get this error:
"Could not find the source association(s) :participant or
:participants in model IncidentParticipant. Try 'has_many
:participants, :through => :incident_participants, :source => '.
Is it one of ?"
Any ideas?
Try taking out the t.belongs_to and replace with belongs_to.
To create a many to many association you should consider creating an association table. That is to say you will have two 1-M relationships that point to a sort interim table. For instance:
In your first model:
class Example < ActiveRecord::Base
has_and_belongs_to_many :example2
end
In your second model:
class Example2 < ActiveRecord::Base
has_and_belongs_to_many :example
end
Then you need to write a migration to link the two tables together:
class CreateTableExamplesExamples2 < ActiveRecord::Migration
create_table :examples_examples2 do |t|
t.integer :example_id
t.integer :example2_id
end
end
Then just let rails magic work. Check out the guides for more information.