I find this issue when I try to save the a team, how can I solve it? I am trying to deal with these associations for so long, if you guys do other issues, just let me know, please (this association has been a nightmare).
Here are the models
class Field < ApplicationRecord
end
class Game < ApplicationRecord
belongs_to :field
belongs_to :organiser
has_one :team
has_many :players, through: :team
end
class Organiser < ApplicationRecord
has_many :games
end
class Player < ApplicationRecord
has_many :teams
has_many :games, through: :teams
end
class Team < ApplicationRecord
has_many :players
belongs_to :game
end
Here are the migrations
class CreateOrganisers < ActiveRecord::Migration[5.2]
def change
create_table :organisers do |t|
t.string :name
t.string :email
t.integer :age
t.timestamps
end
end
end
class CreatePlayers < ActiveRecord::Migration[5.2]
def change
create_table :players do |t|
t.string :name
t.integer :age
t.string :address
t.timestamps
end
end
end
class CreatePlayers < ActiveRecord::Migration[5.2]
def change
create_table :players do |t|
t.string :name
t.integer :age
t.string :address
t.timestamps
end
end
end
class CreateFields < ActiveRecord::Migration[5.2]
def change
create_table :fields do |t|
t.string :location
t.string :transports
t.timestamps
end
end
end
class CreateGames < ActiveRecord::Migration[5.2]
def change
create_table :games do |t|
t.references :field, foreign_key: true
t.references :organiser, foreign_key: true
t.integer :size
t.timestamps
end
end
end
class CreateTeams < ActiveRecord::Migration[5.2]
def change
create_table :teams do |t|
t.references :player, foreign_key: true
t.references :game, foreign_key: true
t.timestamps
end
end
end
class AddTeamToGames < ActiveRecord::Migration[5.2]
def change
add_column :games, :team, :reference
end
end
The idea is to make sure that each game will have a team of certain people. I want to access the people through game.team.player
Your migrations doesn't have any kind of references to connect these tables.
For example Game has_one :team is not going to create this references for you, to call game.team needs a column called game_id in teams table.
To add the reference you can create a migration for it:
rails g migration AddGameToTeams game:references
This will create that migration file may look like this:
class AddGameToTeams < ActiveRecord::Migration
def change
add_reference :teams, :game, index: true
end
end
Depending in the version you running rails this migration file may differ a little with adding an extra line regarding foreign_key.
Run the generated migration with rails db:migrate and it should work.
You need to apply the same concept to the rest of your tables.
Hope this helps.
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'm doing like a little video site like 9gagtv style
and videos in the site has a category so the user can find all the Tech videos for example
but some videos belongs_to_many categories like when a video is about tech but also is funny so it will show up in both categories videos and i'm not sure how to do this?
would it require more than one t.references in videos table ? how would the relationship go?
category.rb
class Category < ApplicationRecord
has_many :videos
end
video.rb
class Video < ApplicationRecord
belongs_to :category
end
category migrations
class CreateCategories < ActiveRecord::Migration[5.0]
def change
create_table :categories do |t|
t.string :title
t.timestamps
end
end
end
video migrations
class CreateVideos < ActiveRecord::Migration[5.0]
def change
create_table :videos do |t|
t.string :url
t.string :title
t.text :description
t.integer :duration
t.references :category, foreign_key: true
t.timestamps
end
end
end
You can use has_and_belongs_to_many
to create many-to-many association through the third table.
Models:
class Category < ApplicationRecord
has_and_belongs_to_many :videos
end
class Video < ApplicationRecord
has_and_belongs_to_many :categories
end
Migrations:
class CreateCategories < ActiveRecord::Migration[5.0]
def change
create_table :categories do |t|
t.string :title
t.timestamps
end
end
end
class CreateVideos < ActiveRecord::Migration[5.0]
def change
create_table :videos do |t|
t.string :url
t.string :title
t.text :description
t.integer :duration
t.timestamps
end
end
end
class CreateCategoriesVideos < ActiveRecord::Migration[5.0]
def change
create_table :categories_videos do |t|
t.references :category, index: true
t.references :video, index: true
end
end
end
I think what you're looking for is has_and_belongs_to_many relation (see more)
It should look something like that
Category
class Category < ApplicationRecord
has_and_belongs_to_many:videos
end
Video
class Video < ApplicationRecord
belongs_to :category
end
And migration
class CreateCategoriesVideosJoinTable < ActiveRecord::Migration
def change
create_table :categories_videos, id: false do |t|
t.integer :category_id
t.integer :video_id
end
end
end
Very new to Rails, have managed a few simple projects, but now stepping into more complex associations between tables and was hoping for some help.
The scenario can best be related to a sports match. Let's say we have
1) A Team (has_many players)
2) A Player (belongs_to team)
3) A Match -- now it gets tricky.
A Match will have: 2 teams, and 22 players (11 on each side) that take part in it. Also, associated with each player, will be their scores for the match (for example, Shots on goal, Goals scored, Points, etc.)
What would be the best practice to create this kind of association? Any tips would be greatly appreciated.
Models
app/models/team.rb
class Team < ActiveRecord::Base
has_many :players, inverse_of: :team
has_many :team_matches
has_many :matches, through: :team_matches
end
app/models/player.rb
class Player < ActiveRecord::Base
belongs_to :team, inverse_of: :player
has_many :player_matches
has_many :matches, through: :player_matches
end
app/models/match.rb
class Match < ActiveRecord::Base
has_many :players, through: :player_matches
has_many :teams, through: :team_matches
end
app/models/team_match.rb
class TeamMatch < ActiveRecord::Base
belongs_to :team
belongs_to :match
end
app/models/player_match.rb
class PlayerMatch < ActiveRecord::Base
belongs_to :player
belongs_to :match
end
Migrations
db/migrate/create_matches.rb
class CreateMatches < ActiveRecord::Migration
def change
create_table :matches do |t|
t.datetime :happened_at
t.timestamps
end
end
end
db/migrate/create_players.rb
class CreatePlayers < ActiveRecord::Migration
def change
create_table :players do |t|
t.string :name
t.timestamps
end
end
end
db/migrate/create_teams.rb
class CreateTeams < ActiveRecord::Migration
def change
create_table :teams do |t|
t.string :name
t.timestamps
end
end
end
db/migrate/create_player_matches.rb
class CreatePlayerMatches < ActiveRecord::Migration
def change
create_table :player_matches do |t|
t.integer :match_id
t.integer :player_id
t.integer :player_shots_on_goal
t.integer :player_goals_scored
t.timestamps
end
end
end
db/migrate/create_team_matches.rb
class CreateTeamMatches < ActiveRecord::Migration
def change
create_table :team_matches do |t|
t.integer :match_id
t.integer :team_id
t.integer :team_points
t.timestamps
end
end
end
Edit1: #Mischa should share credit here! :)
Edit2: Sorry about the many versions, I totally underestimated this problem.
Player Has and belongs to Many Match
That table should contain the details of the player playing that match. For example for which team he played, from which minute (since players can change) etc.
I'm stumped. I keep getting a Called id for nil error Assume i have the following models:
class User < ActiveRecord::Base
self.primary_key = 'name'
attr_accessible :name
has_many :projects, :through => :user_projects
has_many :user_projects
end
class UserProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
after_save do |r|
puts r.user.id #<<<<<error here!
end
end
class Project < ActiveRecord::Base
attr_accessible :name#, :body
has_many :user_projects
has_many :users, :through=> :user_projects
# attr_accessible :title, :body
end
and the following migrations:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :name
t.timestamps
end
end
end
class CreateProjects < ActiveRecord::Migration
def change
create_table :projects do |t|
t.string :name
t.timestamps
end
end
end
class CreateUserProjects < ActiveRecord::Migration
def change
create_table :user_projects do |t|
t.references :user
t.references :project
t.timestamps
end
end
end
running something like :
#project = Factory.create(:project)
#user = Factory.create(:user)
#user.projects << #project
I would get this:
RuntimeError: Called id for nil, which would mistakenly be 4 -- if you really wanted the id of nil, use object_id
Why does the the after_save callback break and what can i do to fix it? It seems like i can't refer to the associated user object from the callback at all. However, if i remove
self.primary_key = 'name'
from the User model, everything works fine. I'm missing something but i don't know what.
Thanks in advance! btw im on rails 3.2.6.
Try to set id to false in your migration like this :
class CreateUsers < ActiveRecord::Migration
def change
create_table :users, :id => false do |t|
t.string :name
t.timestamps
end
end
end
Thanks for the inspiration Dougui! i figured it out. the t.references :project helper defaults the foreign_key to an integer. I changed it manually to the correct type. So now it works!
YaY
class CreateUserProjects < ActiveRecord::Migration
def change
create_table :user_projects do |t|
t.string :user_id #<<<<<<< STRING!!
t.references :project
t.timestamps
end
end
end
I've got a problem with a has_many :through association, i cant call the u1.UsersProfileAttributes.find_by_ProfileAttribute_name("icq") method, rails means that this method doesn't exist. The method u1.UsersProfileAttributes.find_by_ProfileAttribute_id(3) works correctly. u1 is a user object. I don't know whats the problem, because my associations seems to be okay. Have a look:
class ProfileAttribute < ActiveRecord::Base
has_many :UsersProfileAttributes
has_many :users, :through => :UsersProfileAttributes
end
class User < ActiveRecord::Base
has_many :UsersProfileAttributes
has_many :ProfileAttributes, :through => :UsersProfileAttributes
end
class UsersProfileAttribute < ActiveRecord::Base
belongs_to :user
belongs_to :ProfileAttribute
end
My Migration file:
class CreateProfileAttributes < ActiveRecord::Migration
def self.up
create_table :profile_attributes do |t|
t.string :name
t.integer :profile_group_id
t.timestamps
end
create_table :users_profile_attributes do |t|
t.integer :user_id
t.integer :ProfileAttribute_id
t.string :value
end
end
def self.down
drop_table :profile_attributes
drop_table :users_profile_attributes
end
end
You need to query ProfileAttributes, not UsersProfileAttributes. This should work for you: u1.ProfileAttributes.find_by_name('icq')
u1.UsersProfileAttributes.find_by_ProfileAttribute_id(3) works because ProfileAttribute_id is an attribute of UsersProfileAttribute ... so ActiveRecord builds you a dynamic finder.
u1.UsersProfileAttributes.find_by_ProfileAttribute_name("icq") does not work because ProfileAttribute_name is not an attribute of UsersProfileAttribute.