Rails _id sometimes not added to query - ruby-on-rails

I have a class:
class Route < ActiveRecord::Base
belongs_to :origin, class_name: 'Airport'
belongs_to :destination, class_name: 'Airport'
belongs_to :key_transfer_country, class_name: 'Country'
end
with the corresponding entry in schema.rb of
create_table "routes", force: true do |t|
t.integer "origin_id"
t.integer "destination_id"
t.integer "key_transfer_country_id"
t.datetime "created_at"
t.datetime "updated_at"
end
My Airport model includes:
has_many :from_routes, class_name: 'Route', foreign_key: 'destination_id'
has_many :to_routes, class_name: 'Route', foreign_key: 'origin_id'
and my Country model includes:
has_many :transfers, class_name: 'Route', foreign_key: 'key_transfer_country_id'
My problem comes when I try to create a route. Having set up origin, destination and transfer (which can be nil), I have:
route = Route.find_or_create_by(
origin: origin,
destination: destination,
key_transfer_country: transfer
)
but I get an SQL error
no such column: routes.key_transfer_country: SELECT "routes".* FROM "routes" WHERE "routes"."origin_id" = 6658 AND "routes"."destination_id" = 8025 AND "routes"."key_transfer_country" IS NULL LIMIT 1 (ActiveRecord::StatementInvalid)
So for some reason, Rails has appended _id to origin and destination (as I expected), but, for some reason I don't understand, it hasn't appended it to key_transfer_country, so the SELECT is failing.
Can anybody help me work out why?

try to add the foreign_key to your relationship :key_transfer_country, like this
class Route < ActiveRecord::Base
belongs_to :origin, class_name: 'Airport'
belongs_to :destination, class_name: 'Airport'
belongs_to :key_transfer_country, class_name: 'Country', foreign_key: 'key_transfer_country_id'
end
This is because the name of the foreign key can not be assumed for some reason by the length of the field name. Let's try.
Sorry for my english

Related

How to write associations for a model with two foreign keys for the same relationship?

I've been coming back to a problem I haven't solved on my own. I've got a shipments table with two columns for addresses, namely to_address_id and from_address_id.
After looking at a related question (Same Model for Two belongs_to Associations migration), I am still confused. Here is my work so far. I haven't run the migration. I'm trying to resolve this in my mind before getting worked up about it and waste an hour or two. I really thank you for your help.
class AddAddressFromAndAddressToToAddress < ActiveRecord::Migration[6.0]
def change
add_reference :address, :address_from, null: false
add_reference :address, :address_to, null: false
add_foreign_key :address, :shipments, column: :to_address_id
add_foreign_key :address, :shipments, column: :from_address_id
end
en
If i understand correctly you want add origin and destiny address to shipping, correctly?
In this case:
If you want create a migration to create a relationship
class AddOriginAndDestinyAddressToShipment < ActiveRecord::Migration[6.0]
def change
add_reference :shipments, :from_address, foreign_key: { to_table: :addresses }
add_reference :shipments, :to_address, foreign_key: { to_table: :addresses }
end
end
If you want create a model with a relationship
class CreateShipments < ActiveRecord::Migration[6.0]
def change
create_table :shipments do |t|
t.datetime :deadline
t.references :from_address, null: false, foreign_key: { to_table: :addresses }
t.references :to_address, null: false, foreign_key: { to_table: :addresses }
t.timestamps
end
end
end
And model Shipment must be something like this
class Shipment < ApplicationRecord
belongs_to :to_address, class_name: 'Address'
belongs_to :from_address, class_name: 'Address'
end
I did not need the migration, since the foreign keys were already in place. The one I tried didn't work, and the strong_migrations gem was complaining. I only had to wire it up right.
I wanted to show the way I've saved some confusion, so I can type shipment.to_address or shipment.address_to and get the same result. Thank you.
class Shipment < ApplicationRecord
belongs_to :address_from, class_name: 'Address', foreign_key: :from_address_id
belongs_to :from_address, class_name: 'Address', foreign_key: :from_address_id
belongs_to :address_to, class_name: 'Address', foreign_key: :to_address_id
belongs_to :to_address, class_name: 'Address', foreign_key: :to_address_id
end
class Address < ApplicationRecord
has_many :shipments
end

Rails ActiveRecord two fields each referencing the same table

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

Does anybody know another way to create join table migration and specify a specific name of references columns in join table?

I have these two models:
module Studying
class Student < ApplicationRecord
has_and_belongs_to_many :instructors,
class_name: 'Studying::Instructor',
foreign_key: 'studying_student_id',
association_foreign_key: 'studying_instructor_id'
end
end
module Studying
class Instructor < ApplicationRecord
has_and_belongs_to_many :students,
class_name: 'Studying::Student',
foreign_key: 'studying_instructor_id',
association_foreign_key: 'studying_student_id'
end
end
And for join_table I have generated migration:
def change
create_table :studying_instructors_students, id: false do |t|
t.belongs_to :studying_instructor, index: { name: 'index_instructors_students_on_studying_instructor_id' }
t.belongs_to :studying_student, index: { name: 'index_instructors_students_on_studying_student_id' }
end
end
So all is working fine, but the point is, that my senior comrade tells me that I should not use in models things such as:
foreign_key: 'studying_instructor_id',
and
association_foreign_key: 'studying_student_id'
but instead of these I should use:
foreign_key: 'instructor_id',
and
association_foreign_key: 'student_id'
and in the same way in the first model because this is against the convention.
I do not know how I can do this in this models and in tables (how you already understand tables names in db: studying_instructors and studying_students).
Any advice please ?
You can create relation table this way
def change
create_table :studying_instructors_students, id: false do |t|
t.integer :instructor_id, index: true
t.integer :student_id, index: true
end
end
and then you can use foreign_key: 'instructor_id', and association_foreign_key: 'student_id'
If I get this wrong way, please, feel free to address additional questions.
I would do something like this:
class Studying::Student < ApplicationRecord
has_many :student_instructors, class_name: 'Studying::StudentInstructor'
has_many :instructors, through: :student_instructors, class_name: 'Studying::Instructor'
end
class Studying::Instructor < ApplicationRecord
has_many :student_instructors, class_name: 'Studying::StudentInstructor'
has_many :students, through: :student_instructors, class_name: 'Studying::Instructor'
end
class Studying::StudentInstructor < ApplicationRecord
belongs_to :student, class_name: 'Studying::Student'
belongs_to :instructor, class_name: 'Studying::Instructor'
end
I have three tables here. One for students, second for instructors and third one is a junction table (student_instructors).
Migration for studnt_instructor will look something like this
def change
create_table :student_instuctors, id: false do |t|
t.integer :instructor_id, index: true
t.integer :student_id, index: true
end
end
I have not verified the syntax but this is more of logic explanation.
I hope this helps.

Rails - No proper access to self join model

I have model called Section, and I want section to be able to have many Sections - so a Self Joins seems like a place to start.
I setup my code like this:
Model file
class Section < ApplicationRecord
belongs_to :offer
has_many :offer_items
# Self joins:
has_many :child_sections, class_name: "Section", foreign_key: "parent_id"
belongs_to :parent_section, class_name: "Section", optional: true
end
Migration file
class CreateSections < ActiveRecord::Migration[5.0]
def change
create_table :sections do |t|
t.string :name
t.references :offer, foreign_key: true
t.references :parent_section, foreign_key: true
t.timestamps
end
end
end
As you can see I set belongs_to :parent_section as optional, as no every section should has its parent.
When I print attribute_names of my Section model it says:
=> ["id", "name", "offer_id", "parent_section_id", "created_at", "updated_at"]
Trying to retrieve child_sections gets me an error:
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: sections.parent_id: SELECT "sections".* FROM "sections" WHERE "sections"."parent_id" = ?
Where did I make my mistake?
You do not have parent_id in your sections
change this:
has_many :child_sections, class_name: 'Section', foreign_key: 'parent_id'
on this:
has_many :child_sections, class_name: 'Section', foreign_key: 'parent_section_id'

References in Rails using custom Name?

So I am developing a rails app that will have two kinds of Users, student/tutor. but I only have one User model (using cancan for auth), so when I try to set up the meeting model (which has one tutor and one student) how do I do this? This is the model:
class Meeting < ActiveRecord::Base
belongs_to :student
belongs_to :tutor
attr_accessible :price, :subject, :time
end
and here's the relevant part of the schema:
create_table "meetings", :force => true do |t|
t.string "subject"
t.integer "student_id"
t.integer "tutor_id"
t.datetime "time"
t.integer "price"
t.datetime "created_at", :null => false
t.datetime "updated_at", :null => false
end
add_index "meetings", ["student_id"], :name => "index_meetings_on_student_id"
add_index "meetings", ["tutor_id"], :name => "index_meetings_on_tutor_id"
Without having to have two extra models containing student and tutor can I use those labels?
one way to do it..
class Meeting < ActiveRecord::Base
belongs to :student, class_name: 'User'
belongs to :tutor, class_name: 'User'
class User < ActiveRecord::Base
has_many :meet_with_students, class_name: 'Meeting', foreign_key: :tutor_id
has_many :students, through: :meet_with_students, source: :student
has_many :meet_with_tutors, class_name: 'Meeting', foreign_key: :student_id
has_many :tutors, through: :meet_with_tutors:, source: :tutor
I think you're looking for class_name:
class Meeting < ActiveRecord::Base
belongs_to :student, class_name: "User"
belongs_to :tutor, class_name: "User"
end

Resources