Rails has_many undefined method for association - ruby-on-rails

I have two objects: Team and Player. As you could probably guess, a Team has many Players and a Player belongs to a team.
I understand that we can model this relationship with another model, Team_Players but I want to focus on the raw relationship here, as shown by many of the guides I'm seeing online.
When creating Players, it's easy to assign them a Team.id, since they only have one, but the reverse -has_many- is more complicated. The Rails guide on associations only shows the model file, so this is what mine looks like in reflection:
class Team < ActiveRecord::Base
has_many :players
end
Now, I would expect to be able to do something like Team.first.players and be given an array or something, but instead I just get undefined method player for #<Team:0x> and in fact in this video, I do see a developer doing something just like that. So what am I missing? Do I have to make an intersection model here? I would imagine not since has_many is inherent in Rails.
After creating the tables, I added the reference for Team to Player in this migration:
def change
add_reference :players, :team, index: true
add_foreign_key :players, :team
end
Again, since the has many relationship can't be modeled with a single column, I avoided that part in the migration. Is that what's necessary for the desired functionality of Team.first.players returning an array or something?

Here's what I did to get this to work:
rails new teams - followed by bundle
rails g model Team name:string player_count:integer
rails g model Player player_name:string player_number:integer
rails g migration add_team_id_to_players:
class AddTeamIdToPlayers < ActiveRecord::Migration
def change
add_column :players, :team_id, :integer
end
end
rake db:migrate
Here are my models:
class Player < ActiveRecord::Base
belongs_to :team
end
class Team < ActiveRecord::Base
has_many :players
end
Then, in the console:
Team.create(name: "Cats", player_count:1).save
Player.create(player_name: "Ryan", player_number:1, team_id:1).save
Then voila:
Team.first.players returns:
Team Load (0.2ms) SELECT "teams".* FROM "teams" ORDER BY "teams"."id" ASC LIMIT 1
Player Load (0.1ms) SELECT "players".* FROM "players" WHERE "players"."team_id" = ? [["team_id", 1]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Player id: 2, player_name: "ryan", player_number: 1, created_at: "2015-12-18 19:32:39", updated_at: "2015-12-18 19:32:56", team_id: 1>]>

Related

Rails and polymorphic associations

I have three models: User, Company, and Subscription. What I am trying to accomplish is a Subscription belongs to either a User OR a Company.
To try accomplish this, I referenced this guide, but I have been unsuccessful as the record creation keeps rolling back.
here's my Company model:
# app/models/company.rb
class Company < ApplicationRecord
has_many :subscriptions, dependent: :destroy, as: :imageable
end
here's my User model:
# app/models/user.rb
class User < ApplicationRecord
has_many :subscriptions, dependent: :destroy, as: :imageable
end
and finally, here's my Subscription model:
class Subscription < ApplicationRecord
belongs_to :imageable, polymorphic: true
end
Now as far as the migration file, this is my Subscription migration file:
class CreateSubscriptions < ActiveRecord::Migration[5.1]
def change
create_table :subscriptions do |t|
t.references :imageable, polymorphic: true, index: true
t.date :start_date
t.date :stop_date
t.timestamps
end
end
end
As far as what I can see, this is pretty much exactly like the guide shows, but it keeps rolling back. Here's the output of the rails console:
Loading development environment (Rails 5.1.6)
2.5.1 :001 > Subscription.create(imageable_id: 1, start_date: Time.now, stop_date: 2.days.from_now)
(8.6ms) SET NAMES utf8, ##SESSION.sql_mode = CONCAT(CONCAT(##sql_mode, ',STRICT_ALL_TABLES'), ',NO_AUTO_VALUE_ON_ZERO'), ##SESSION.sql_auto_is_null = 0, ##SESSION.wait_timeout = 2147483
(0.2ms) BEGIN
(0.3ms) ROLLBACK
=> #<Subscription id: nil, imageable_type: nil, imageable_id: 1, start_date: "2018-10-10", stop_date: "2018-10-12", created_at: nil, updated_at: nil>
2.5.1 :002 >
Here are the questions that I have:
Why is there an imageable_type field? Is that created by t.references and if so, do I need this? Can I just use imageable_id instead of t.references as the other part of the suggestion shows?
Why is it rolling back? Are polymorphic associations done differently in Rails 5.x or something by chance?
According to the graph shown in the guide, it looks like if a picture belongs to imageable_id 4, then if there is an employee AND a production with the ID of 4, then a picture would belongs to both instead of one or the other like I'm trying to accomplish. Correct?
In your association, Imageable type will contain the class name and imageble id will contain the id of that class. So if you want to create subscription for user you can do like below
User.first.subcriptions.create(start_date: Time.now, stop_date: 2.days.from_now)
So it will automatically pick up First user's id in imageable id and take "User" as imageable type.
If you want to create subscription manually, you must have to pass both fields imageable type and imageble id like below,
Subscription.create(imageable_id: 1, imageable_type: "User", start_date: Time.now, stop_date: 2.days.from_now)
Why is there an imageable_type field? Is that created by
t.references and if so, do I need this? Can I just use imageable_id
instead of t.references as the other part of the suggestion shows?
=> imageable_type will contain the class of associate model like "User" or "Company"
Why is it rolling back? Are polymorphic associations done
differently in Rails 5.x or something by chance?
=> No, you setup it correctly
According to the graph shown in the guide, it looks like if a picture belongs to imageable_id 4, then if there is an employee AND a production with the ID of 4, then a picture would belongs to both instead of one or the other like I'm trying to accomplish. Correct ?
=> It depends on both imageable_id and imageble_type , so by combination of both this you will get record. If imageable_id is 4 and imageable_type is "Picture" then it will take Picture with id 4.
Please check this link for understaing
For polymorphic association, you should also pass imageable_type along with imageable_id. You don't do it and that's why it doesn't work, most probably (i.e. there might be other reasons, I don't know, but this one is pretty obvious).
imageable_type holds the name of the class of the record given Subscription is associated to.

Rails ActiveRecord access record through has_many association using a different key

Scenario
I'm building an admin panel in Rails for an old app that i wrote a while ago therefore the database is not designed to work with ActiveRecord.
The design
As you can see the foreign key which is used by the Game and Refferal table is NOT the id of the User but instead a column on the User which is called steam_id
The Rails Model:
User class
has_many :games, :foreign_key => 'player'
The problem
When i'm trying the following command:
User.find(1).games
the ActiveRecord is producing the following queries:
User Load (0.0ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 LIMIT 1
Game Load (1012.0ms) SELECT `games`.* FROM `games` WHERE `games`.`player` = '1'
As you can see on the second query it tries to find a game where the player column is equal to 1.
Obviously this is not what I want. I want to get the game where the game.player is equal to user.steam_id
How can i fix that? And most importantly how can i also fix the belongs_to association?
Considerations
The main website is already live and with active users. I cant just start redesigning the entire DB and the codebase itself.
Try specifying the primary_key in the association:
User model:
has_many :games, primary_key: 'steam_id', foreign_key: 'player'
Game model:
belongs_to :user, primary_key: 'steam_id', foreign_key: 'player'
Now you should be able use User.find(1).games, which will query for user's games using steam_id instead of id.
You can Run 2 separate queries:
user = User.find(1)
games = Games.where(player: user.stream_id)
User class:
has_many :games, :foreign_key => 'player'
Game class:
belongs_to :player, class_name: 'User'
This might help you give a try.

Rails has one association

I am working on building a portfolio section in my website right now. I have it set where a portfolio has one category that is associated with it.
class Portfolio < ApplicationRecord
has_one :category
end
I am trying to access the category attribute name in the show view for the portfolio , but I am getting this error.
SQLite3::SQLException: no such column: categories.portfolio_id: SELECT "categories".* FROM "categories" WHERE "categories"."portfolio_id" = ? LIMIT ?
This is what is in the view:
<li><i class="icon_link"></i><span>Category: </span><%= #portfolio.category.name %></li>
I remembering using this syntax before and not having any problems with it. Any help would be great. I tried to find this question on here =, but could not make of them work.
Rails conventional naming for has_one/belongs_to associations is - if you have category_id column in Portfolio then "a portfolio belongs_to a category" and "a category has one portfolio". You need to rewrite your models as:
class Portfolio < ApplicationRecord
belongs_to :category
class Category < ApplicationRecord
has_one :portfolio
After that #portfolio.category.name should work fine.
Update: Possibly "a category has many portfolios", when the models will be:
class Portfolio < ApplicationRecord
belongs_to :category
class Category < ApplicationRecord
has_many :portfolio
Whenever you have an association in rails, there has to be at least two entites (or models) involved in that association. In your particular case, you've associated a Portfolio with a Category via the has_one association. However, you need to specify the association on the Category end as well. So in your Category.rb model class, you need to write:
class Category < ActiveRecord::Base
belongs_to :portfolio
# other stuff
end
There's one more step to ensuring that the association is working properly. You need to make sure that the categories table in your database has a field called portfolio_id. If it does, then you should be good to go! If it doesn't then do the following:
Type rails generate migration addPortfolioIdToCategory in your terminal
Open up the migration file and ensure it looks like this:
def change
add_column :categories, :portfolio_id, :integer
end
Now run rake db:migrate from your terminal
If you reload your server, your problem should be solved!
NOTE
The model that specifies the belongs_to association must have the primary key attribute in the corresponding database table. In your case, if the Category model has the belongs_to association, then the categories database table must have the field titled portfolio_id.
It's complaining portfolio_id doesn't exist on your categories table. Verify your migration file for categories. Given the foreign key is set on categories it should work. Rails has one association

Multiple Associations in a Model

I have a User model and an Account model. The user has many accounts and the accounts belong to one user. I have the models and associations all set up. Now I want to make one of those accounts the "primary account". What is the best way to set up the associations? I added a primary_account_id column to my user table and set up the associations like this but it didn't work. Any tips?
class User < ActiveRecord::Base
has_many :accounts
has_one :primary_account, :class_name => "Account"
end
class Account < ActiveRecord::Base
belongs_to :user
end
Edit
I see this question Rails model that has both 'has_one' and 'has_many' but with some contraints which is very similar and the second answer makes the suggestion that I tried. However when I use it rails ignores the column that I've made and just grabs the first one in the table:
>> u = User.find(1)
User Load (3.9ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? LIMIT 1 [["id", 1]]
=> #<User id: 1, email: "XXXXXXX#gmail.com", created_at: "2012-03-15 22:34:39", updated_at: "2012-03-15 22:34:39", primary_account_id: nil>
>> u.primary_account
Account Load (0.1ms) SELECT "accounts".* FROM "accounts" WHERE "accounts"."user_id" = 1 LIMIT 1
=> #<Account id: 5, name: "XXXXXX", created_at: "2012-03-16 04:08:33", updated_at: "2012-03-16 17:57:53", user_id: 1>
>>
So I created a simple ERD and your issue is very simple, but I think I found a serious issue:
class User < ActiveRecord::Base
has_many :accounts
has_one :primary_account, :class_name => "Account", :primary_key => "account_pimary_id"
end
class Account < ActiveRecord::Base
belongs_to :user
end
To get the associations as is, just set the :primary_key on has_one :primary_account so that it uses users.account_primary_id instead of users.id.
While this works, it will proboably cause nothing but problems. If Account's user_id is used as the foreign key for id and account_primary_id, you have no idea if an Account is a normal Account or a Primary Account without explicitly joining both id and account_primary_id every time. A foreign_key should only point at 1 column, in this case, User's table id. Then it is a straight shot into the Account's table.
#Zabba solution is the smart one, but just needs the :include for the join
has_one :primary_account, :class_name => "Account", :conditions => "users.primary_account_id = accounts.id", :include => :user
This means all Accounts belong to a User and only 1 is flagged as a primary account. Nice and straight forward, avoiding the wacky where clauses.

Trouble with rails naming conventions

I think I have followed the rails naming conventions in my app. But when I am in the terminal testing code I am coming across some errors that go against naming conventions. Here is my terminal session:
irb(main):010:0> a = _
=> #<Neighborhood id: 24, name: "Lincoln Park", created_at: "2011-12-03 20:29:00", updated_at: "2011-12-03 21:08:47", minlat: 41.91092, maxlat: 41.925658, minlng: -87.648761, maxlng: -87.636117>
irb(main):011:0> a.cta_trains
NoMethodError: undefined method `cta_trains' for #<Neighborhood:0x007fd666ee61e8>
from /usr/local/Cellar/ruby/1.9.2-p290/lib/ruby/gems/1.9.1/gems/activemodel-3.1.1/lib/active_model/attribute_methods.rb:385:in `method_missing'
Now when I try a.CtaTrains:
irb(main):012:0> a.CtaTrains
CtaTrain Load (0.4ms) SELECT "cta_trains".* FROM "cta_trains" INNER JOIN "cta_locations" ON "cta_trains"."id" = "cta_locations"."CtaTrain_id" WHERE "cta_locations"."neighborhood_id" = 24
SQLite3::SQLException: no such column: cta_locations.CtaTrain_id: SELECT "cta_trains".* FROM "cta_trains" INNER JOIN "cta_locations" ON "cta_trains"."id" = "cta_locations"."CtaTrain_id" WHERE "cta_locations"."neighborhood_id" = 24
ActiveRecord::StatementInvalid: SQLite3::SQLException: no such column: cta_locations.CtaTrain_id: SELECT "cta_trains".* FROM "cta_trains" INNER JOIN "cta_locations" ON "cta_trains"."id" = "cta_locations"."CtaTrain_id" WHERE "cta_locations"."neighborhood_id" = 24
From my models:
class Neighborhood < ActiveRecord::Base
has_many :cta_trains, :through => :cta_locations
has_many :cta_locations, :foreign_key => :neighborhood_id
end
class CtaTrain < ActiveRecord::Base
has_many :neighborhoods, :through => :cta_locations
has_many :cta_locations, :foreign_key => :cta_train_id
end
class CtaLocation < ActiveRecord::Base
belongs_to :neighborhood
belongs_to :cta_train
end
I am at a standstill, stuck, banging my head against the wall, etc. Any help would be fabulous.
Rails noobie here....as if this point is not obvious.....
Noticed that you appear to be in IRB... Instead, I'd try to stay in the rails console when working with your active-record classes.
so start that with
bundle exec rails console
What you need here is a junction table. See the association has_and_belongs_to_many.
The junction table will store the links between a certain Neighbourhood and a certain CtaTrain. Here, it's CtaLocation but if you don't plan to actually use this model, you could even not define it.
For instance, you can achieve it With three tables (neighbourhoods, cta_trains and cta_trains_neighbourhoods) and only two models like :
class Neighbourhood
has_and_belongs_to_many :cta_trains
end
class CtaTrain
has_and_belongs_to_many :neighbourhoods
end

Resources