Rails 2.3.8 Association Problem has_many belongs_to - ruby-on-rails

I'm new to Rails. I have two models, Person and Day.
class Person < ActiveRecord::Base
has_many :days
end
class Day < ActiveRecord::Base
belongs_to :person
has_many :runs
end
When I try to access #person.days I get an SQL error:
$ script/consoleLoading development environment (Rails 2.3.8)
ree-1.8.7-2010.02 > #person = Person.first
=> #<Person id: 1, first_name: "John", last_name: "Smith", created_at: "2010-08-29 14:05:50", updated_at: "2010-08-29 14:05:50"> ree-1.8.7-2010.02
> #person.days
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: days.person_id: SELECT * FROM "days" WHERE ("days".person_id = 1)
I setup the association between the two before running any migrations, so I don't see why this has not been setup correctly.
Any suggestions?

Telling your model about the association doesn't set up the foreign key in the database - you need to create an explicit migration to add a foreign key to whichever table is appropriate.
For this I'd suggest:
script/generate migration add_person_id_to_days person_id:integer
then take a look at the migration file it creates for you to check it's ok, it should be something like this:
class AddPersonIdToDays < ActiveRecord::Migration
def self.up
add_column :days, :person_id, :integer
end
def self.down
remove_column :days, :person_id
end
end
Run that and try the association again?

Related

Rails self join with has_many association

I am trying to write an app like IMDB in rails.
I have created the Movie model. Every movie has many movie recommendations (which are also instances of Movie).
I don't know how to add the "has_many" association, how to write the migration file or how to add recommended movies to each movie.
You have a many-to-many relationship, which means we need a join table Recommendation.
Create model and migration files with a generator:
bin/rails generate model Movie
bin/rails generate model Recommendation
Then update migrations:
# db/migrate/20221023063944_create_movies.rb
class CreateMovies < ActiveRecord::Migration[7.0]
def change
create_table :movies do |t|
# TODO: add fields
end
end
end
# db/migrate/20221023064241_create_recommendations.rb
class CreateRecommendations < ActiveRecord::Migration[7.0]
def change
create_table :recommendations do |t|
t.references :movie, null: false, foreign_key: true
t.references :recommended_movie, null: false, foreign_key: { to_table: :movies }
end
end
end
Run migrations:
bin/rails db:migrate
Setup models:
# app/models/movie.rb
class Movie < ApplicationRecord
# NOTE: this is the relationship for join table
has_many :recommendations, dependent: :destroy
# NOTE: get movies from join table
has_many :recommended_movies, through: :recommendations
# this ^ is the name of the relationship in `Recommendation` we want
end
# app/models/recommendation.rb
class Recommendation < ApplicationRecord
belongs_to :movie
# NOTE: our foreign key is `recommended_movie_id` which rails infers
# from `:recommended_movie`, but we have to specify the class:
belongs_to :recommended_movie, class_name: "Movie"
end
Test it in the console bin/rails console:
>> 3.times { Movie.create }
>> Movie.first.recommended_movies << [Movie.second, Movie.third]
>> Movie.first.recommended_movies
=> [#<Movie:0x00007f15802ec4c0 id: 2>, #<Movie:0x00007f15802ec3d0 id: 3>]
or like this:
>> Movie.second.recommendations << Recommendation.new(recommended_movie: Movie.first)
>> Movie.second.recommended_movies
=> [#<Movie:0x00007f158215ef20 id: 1>]
https://guides.rubyonrails.org/association_basics.html#the-has-many-through-association
https://guides.rubyonrails.org/association_basics.html#self-joins
When creating a migration you need to define which model reference you want to assign
create_table :student do |t|
t.references :class, foreign_key: true
end
Here I am telling my class table to store the primary key of student as a foreign key after migration there will be a column in class named student_id which stores pk of the student table.
Then I will define an association in class model file
class student < ApplicationRecord
belongs_to :class
end
This will help me in query so I can write
student= Student.find 'student_id'
class = student.class
This will return the class of that student. For has_many the procedure is same but it will return you the array

Rails migration issue column

I have simple migration for add column
class AddSeatGroupForFee < ActiveRecord::Migration
def up
add_column :fees, :seat_group_id, :integer
end
end
my Fee model
belongs_to :seat_group
my SeatGroup
has_many :fees
and when i want to use this column in next migration
i haven't this column
when i stop with binding.pry
seat_group_id
should be presents
Fee
=> Fee(id: integer, code: string, created_at: datetime, updated_at: datetime)
and Fee hasn't seat_group_id column here
world_business = SeatGroup.create({name: '(name)', airline_code: 'code'})
world_business.fees.where(code: 'W-BUSINESS') => error here
why it happens?
rails 4
Fee.reset_column_information
fixed my problem

Rails string as a foreign key

I have a relation between User and Course (typical enrollment data). A User has_many Course and vice-versa (typical JOIN table scenario).
I am attempting to migrate my previous has_and_belongs_to_many relationship between these two models to a has_many :through relationship. My files currently look like:
class User < ActiveRecord::Base
has_and_belongs_to_many :courses
end
and
class Course < ActiveRecord::Base
has_and_belongs_to_many :users
end
and the table name that joins the two models is courses_users.
I now need to migrate this relationship to the has_many :through association, and also make the column type of user_id a string, as I want to use the g_number (string) attribute of User as the foreign key. Note: I don't care about the performance difference between int and varchar/string.
The short and simple problem is that I need users.g_number to reference enrollments.user_id as a foreign key, and both are strings.
My attempt at a migration and model rework is this:
class User < ActiveRecord::Base
has_many :enrollment
has_many :courses, :through => :enrollment
end
and
class Course < ActiveRecord::Base
has_many :enrollment
has_many :users, :through => :enrollment
end
lastly
class Enrollment < ActiveRecord::Base
belongs_to :course
belongs_to :user
end
then the migration
class ChangeUserIdJoin < ActiveRecord::Migration
def self.up
rename_table :courses_users, :enrollments
end
def self.down
rename_table :enrollments, :courses_users
end
end
Everything works fine here. I can do queries like User.courses and Course.users. But now I want to change the type of the user_id column in the join table to a string so that I can store the g_number (string attribute on User) and join on that instead of the serial id column of User.
When I attempt to change the user_id column type to string in the migration:
class ChangeUserIdJoin < ActiveRecord::Migration
def self.up
change_column :courses_users, :user_id, :string
rename_table :courses_users, :enrollments
end
def self.down
rename_table :enrollments, :courses_users
change_column :courses_users, :user_id, :integer
end
end
the queries Course.users and User.courses start failing (below from Rails console). User.courses returns an empty array (whereas before there are multiple Course objects), and Course.users throws an exception because of mismatched column types (which obviously makes sense):
u = User.take
User Load (0.9ms) SELECT "users".* FROM "users" LIMIT 1
=> #<User id: 1, username: "director", g_number: "g00000000", password_digest: "$2a$10$dvcOd3rHfbcR1Rn/D6VhsOokj4XiIkQbHxXLYjy5s4f...", created_at: "2016-01-06 01:36:00", updated_at: "2016-01-06 01:36:00", first_name: "Director", last_name: "", role: 0, registered: true>
2.1.5 :002 > u.courses
Course Load (0.9ms) SELECT "courses".* FROM "courses" INNER JOIN "enrollments ON "courses"."id" = "enrollments"."course_id" WHERE "enrollments"."user_id" = $1 [["user_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy []>
2.1.5 :003 > c = Course.take
Course Load (0.7ms) SELECT "courses".* FROM "courses" LIMIT 1
=> #<Course id: 12754, year: 2015, semester: 0, department: 7, course: 101, section: 1, name: "SPA 101 01 - Elementary Spanish I">
2.1.5 :004 > c.users
PG::UndefinedFunction: ERROR: operator does not exist: integer = character varying
LINE 1: ... "users" INNER JOIN "enrollments" ON "users"."id" = "enrollm...
I need to be able to join on enrollments.user_id = users.g_number. What do I need to do in order to change the user_id column to a string type in the Enrollment model/table, and still be able to do Active Record queries like User.courses and Course.users?
Try by specifying the foreign and primary keys in enrollments model, like this
belongs_to :user foreign_key: :user_id, primary_key: :g_number

Newbie Rails HABTM association not working

I'm new to Rails and trying to create a has_and_belongs_to_many relationship between orders and items.
class Order < ActiveRecord::Base
has_and_belongs_to_many :items
end
class Item < ActiveRecord::Base
has_and_belongs_to_many :orders
end
Migration for Orders (not shown. very basic)
Migration for OrderItems:
class CreateItems < ActiveRecord::Migration
def self.up
create_table :items do |t|
t.string :name
t.decimal :price
t.timestamps
end
create_table :items_orders, :id => false do |t|
t.references :item, :order
end
end
def self.down
drop_table :items
drop_table :items_orders
end
end
In script/console I'm trying to "prove" that relationship works but either my understanding of Ruby is bad (likely) or my model is.
$ script/console
Loading development environment (Rails 2.3.5)
>> o = Order.new
=> #<Order id: nil, name: nil, created_at: nil, updated_at: nil>
>> o.name = 'first order'
=> "first order"
>> o.save
=> true
>> o.items
=> []
>> i1 = o.items.new
=> #<Item id: nil, name: nil, price: nil, created_at: nil, updated_at: nil>
>> i1.name = 'some widget'
=> "some widget"
>> i1.price = 12.50
=> 12.5
>> i1.save
=> true
>> o.items
=> []
>> o.items.first
=> nil
looking in the development.sqlite3 database:
$ sqlite3 development.sqlite3
SQLite version 3.6.12
Enter ".help" for instructions
Enter SQL statements terminated with a ";"
sqlite> .tables
items items_orders orders schema_migrations
sqlite> select * from items_orders;
sqlite> .schema items_orders
CREATE TABLE "items_orders" ("item_id" integer, "order_id" integer);
sqlite>
Nothing!
I know it's obvious...but not to me...at this stage anyway...
what have I missed/screwed up?
First of all, why don't you just use belongs_to and has_many? For example:
class Order < ActiveRecord::Base
has_many :items
end
class Item < ActiveRecord::Base
belongs_to :order
end
As for why you don't get expected results you can try:
order = Order.new
order.save
item = Item.new
item.order = order
item.save
or better
order = Order.create(myordercolumn => "whatever")
order.items.create(:name => "some widget")

Complicted ActiveRecord Association. Going through a 4th table

I have kind of a complicated case and am wondering how this would work in rails:
I want to categories the genres of some singers. Singers can belong to more than one genres, and users can assign tags to each genre
For example:
singers <-- singers_genres --> genres <-- genres_tags --> tags
SQL would look something like:
SELECT * FROM singers S
INNER JOIN singers_genres SG ON S.id=SG.singer_id
INNER JOIN genres G ON G.id = SG.genre_id
LEFT OUTER JOIN genre_tags GT ON G.id = GT.genre_id
INNER JOIN tags T ON GT.tag_id = T.id
Here are what my Classes look like:
class Singer
has_and_belongs_to_many :genres, :include => :tag
class Genre
has_and_belongs_to_many :singers
has_and_belongs_to_many :tags
class Tag
has_and_belongs_to_many :genres
Let's create the project ...
rails itunes
cd itunes
create the basic models:
script/generate model Singer name:string
script/generate model Genre name:string
script/generate model Tag name:string
do the migration:
rake db:migrate
update the models:
class Singer < ActiveRecord::Base
has_and_belongs_to_many :genres
end
class Genre < ActiveRecord::Base
has_and_belongs_to_many :singers
has_and_belongs_to_many :tags
end
class Tag < ActiveRecord::Base
has_and_belongs_to_many :genres
end
create two more migration for joining tables:
script/generate migration CreateGenresSingersJoin
script/generate migration CreateGenresTagsJoin
rake db:migrate
the genres_singers model:
class CreateGenresSingersJoin < ActiveRecord::Migration
create_table 'genres_singers', :id => false do |t|
t.integer 'genre_id'
t.integer 'singer_id'
end
def self.down
drop_table'genres_singers'
end
end
the genres_tags model:
class CreateGenresTagsJoin < ActiveRecord::Migration
create_table 'genres_tags', :id => false do |t|
t.integer 'genre_id'
t.integer 'tag_id'
end
def self.down
drop_table'genres_tags'
end
end
create some seeding data in seeds.db, or whatever means:
Singer.create(:name => 'Lady Ga Ga')
Genre.create(:name => 'Pop')
Genre.create(:name => 'Folk')
Tag.create(:name => 'Top50')
insert some link data:
INSERT INTO genres_singers (genre_id, singer_id) VALUES (1, 1)
INSERT INTO genres_singers (genre_id, singer_id) VALUES (2, 1)
INSERT INTO genres_tags (genre_id, tag_id) VALUES (1, 1)
then we can use the associations, e.g.:
Singer.first.genres.first.tags.first
=> #<Tag id: 1, name: "Top50">
Singer.find_by_name("Lady Ga Ga").genres.first.tags
=> [#<Tag id: 1, name: "Top50">]
Choosing Between has_many :through and has_and_belongs_to_many is a good introduction to associations.
Maybe you can post your models here to see how the associations are designed.

Resources