Trying to seed a has_many/belongs_to relationship.
A user has_many scripts, a script belongs_to a user.
When seeding the script, I get this error:
ActiveModel::UnknownAttributeError: unknown attribute 'user_id' for
Script.
I'd think that this line in my migration would create a user_id attribute for script?
t.belongs_to :user, index: true, foreign_key: true
app/models/script.rb
class Script < ApplicationRecord
belongs_to :user
has_many :commits
has_many :script_users, through: :commits, source: :user
end
app/models/user.rb
class User < ActiveRecord::Base
has_many :scripts
has_many :used_scripts, through: :commits, source: :script
has_many :commits
end
db/migrate/20171231022826_create_scripts.rb
class CreateScripts < ActiveRecord::Migration[5.1]
def change
create_table :scripts do |t|
t.string :name
t.string :skill
t.string :bot_for
t.string :game_for
t.belongs_to :user, index: true, foreign_key: true
t.timestamps
end
end
end
db/seeds.rb
admin = User.create!(
email: 'a#a.com',
password: 'adminadmin',
password_confirmation: 'adminadmin'
)
script = Script.create!(
name: 'YWoodcutter',
skill: 'Woodcutting',
bot_for: 'Android',
game_for: "CandyLand,
user_id: admin.id
)
ActiveModel::UnknownAttributeError: unknown attribute 'user_id' for
Script.
I've made sure to rake:db reset and still get error.
Thank you
UPDATE:
I did a few things and it's working OK now. I believe the issue may have been that my user migration was created after my script migration. The user migration should done before scripts, so I changed the dates to change the order they were migrated in. Did a rake rake db:rollback & db:reset for good measure too.
belongs_to is an alias of reference and it should work same as references
If its not working for you just try with following solution
class CreateScripts < ActiveRecord::Migration[5.1]
def change
create_table :scripts do |t|
t.string :name
t.string :skill
t.string :bot_for
t.string :game_for
t.references :user, index: true, foreign_key: true
t.timestamps
end
end
end
Create Model with below line
rails g model script references:user
It will by default below line in migration file
t.references :user, foreign_key: true
and add belongs_to :user in script model . It might be t.references :user in rails 5 not t.belongs_to :user
Related
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 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?
I am using PostgreSQL as my db in rails. I have a User model and a Product model. I am trying to make a Transaction model where I want to reference user twice, once as buyer and once as seller. I generated the models as suggested in this post (answer by toasterlovin)
Write a migration with reference to a model twice
However it gives me an error when I use PostgreSQL (with SQLite it worked fine). I get the following error. What can I do to resolve this?
{StandardError: An error has occurred, this and all later migrations canceled:
PG::UndefinedTable: ERROR: relation "buyers" does not exist
: ALTER TABLE "transactions" ADD CONSTRAINT "fk_rails_0b24a7fcc3"
FOREIGN KEY ("buyer_id")
REFERENCES "buyers" ("id")
C:/Users/Powerhouse/Desktop/CodingDojo/ruby/rails/demo/db/migrate/20161024032156_create_transactions.rb:3:in change'
C:inmigrate'
ActiveRecord::StatementInvalid: PG::UndefinedTable: ERROR: relation "buyers" does not exist
: ALTER TABLE "transactions" ADD CONSTRAINT "fk_rails_0b24a7fcc3"
FOREIGN KEY ("buyer_id")
REFERENCES "buyers" ("id")
C:/Users/Powerhouse/Desktop/CodingDojo/ruby/rails/demo/db/migrate/20161024032156_create_transactions.rb:3:in change'
C:inmigrate'
PG::UndefinedTable: ERROR: relation "buyers" does not exist
C:/Users/Powerhouse/Desktop/CodingDojo/ruby/rails/demo/db/migrate/20161024032156_create_transactions.rb:3:in change'
C:inmigrate'
Tasks: TOP => db:migrate
(See full trace by running task with --trace) }
Models
class User < ActiveRecord::Base
has_many :products
has_many :sales, :class_name => 'Transaction', :foreign_key => 'seller_id'
has_many :purchases, :class_name => 'Transaction', :foreign_key => 'buyer_id
end
Product Model
class Product < ActiveRecord::Base
belongs_to :user
end
Transaction model
class Transaction < ActiveRecord::Base
belongs_to :buyer, :class_name => 'User'
belongs_to :seller, :class_name => 'User'
belongs_to :product
end
Migration files
User
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.timestamps null: false
end
end
end
Product
class CreateProducts < ActiveRecord::Migration
def change
create_table :products do |t|
t.references :user, index: true, foreign_key: true
t.string :name
t.timestamps null: false
end
end
end
Transaction
class CreateTransactions < ActiveRecord::Migration
def change
create_table :transactions do |t|
t.references :buyer, index: true, foreign_key: true
t.references :seller, index: true, foreign_key: true
t.references :product, index: true, foreign_key: true
t.timestamps null: false
end
end
end
This is due to PostgreSql not understanding how to make the custom association foreign keys.
Try changing the migration file to,
class CreateTransactions < ActiveRecord::Migration
def change
create_table :transactions do |t|
t.integer :buyer_id
t.integer :seller_id
t.integer :product_id
t.timestamps null: false
end
add_index(:transactions, :buyer_id)
add_index(:transactions, :seller_id)
add_index(:transactions, :product_id)
add_foreign_key :transactions, :users, column: :buyer_id
add_foreign_key :transactions, :users, column: :seller_id
end
end
It automatically links buyer_id to user, taking the power of
belongs_to :buyer, :class_name => 'User'
The above line makes the buyer_id as foreign key.
You can also try,
class CreateTransactions < ActiveRecord::Migration
def change
create_table :transactions do |t|
t.references :buyer, index: true
t.references :seller, index: true
t.references :product, index: true
t.timestamps null: false
end
add_foreign_key :transactions, :users, column: :buyer_id
add_foreign_key :transactions, :users, column: :seller_id
end
end
Your problem is that when ActiveRecord see this:
t.references :buyer, index: true, foreign_key: true
it tries to create a foreign key inside the database (because foreign_key: true) but the naming conventions tell AR that it should be linking to the buyers table (because you're referencing :buyer).
Just like you specify class_name: in your model:
belongs_to :buyer, :class_name => 'User'
to tell AR not use the conventional Buyer name you can override the convention in your migration by modifying your :foreign_key option:
t.references :buyer, index: true, foreign_key: { to_table: :users }
to explicitly name the table. Similarly for your other reference that doesn't use the conventional naming scheme:
t.references :seller, index: true, foreign_key: { to_table: :users }
I have a Project migration class like this:
class CreateProjects < ActiveRecord::Migration
def change
create_table :projects do |t|
t.string :title
t.text :description
t.boolean :public
t.references :user, index: true, foreign_key: true
t.timestamps null: false
end
end
end
It creates a column name user_id in projects table but I want to name the column owner_id so I can use project.owner instead of project.user.
You can do it two ways:
#app/models/project.rb
class Project < ActiveRecord::Base
belongs_to :owner, class_name: "User", foreign_key: :user_id
end
OR
$ rails g migration ChangeForeignKeyForProjects
# db/migrate/change_foreign_key_for_projects.rb
class ChangeForeignKeyForProjects < ActiveRecord::Migration
def change
rename_column :projects, :user_id, :owner_id
end
end
then:
$ rake db:migrate
I have a Message model that has a user and a teammember (both are users).
The models are:
# message.rb
class Message < ActiveRecord::Base
belongs_to :user
belongs_to :teammember, :class_name => "User", :foreign_key => 'teammember_id'
end
# user.rb
class User < ActiveRecord::Base
has_many :messages
end
And I have this migration:
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.references :user, index: true, foreign_key: true
t.references :teammember, index: true, foreign_key: true
t.text :body
t.boolean :read, default: false
t.timestamps null: false
end
end
end
When I run the rake db:migrate locally (with sqlite3) everything works fine.
The problem is that when I deploy to heroku (that uses postgresql) and run
heroku run rake db:migrate
It raises the next error:
PG::UndefinedTable: ERROR: relation "teammembers" does not exist
: ALTER TABLE "messages" ADD CONSTRAINT "fk_rails_7efc67ccc9"
FOREIGN KEY ("teammember_id")
REFERENCES "teammembers" ("id")
Do you know what is the problem and how can I fix it?
Rails guesses based on the association, because you're referencing a table it can't determine from the association you'll have to add it yourself.
class CreateMessages < ActiveRecord::Migration
def change
create_table :messages do |t|
t.references :user, index: true, foreign_key: true
t.references :teammember, index: true
t.text :body
t.boolean :read, default: false
t.timestamps null: false
end
add_foreign_key :messages, :users, column: :teammember_id
end
end