Multiple Associations in a Model - ruby-on-rails

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.

Related

how does has_many :through work with only two models?

Just trying to work out how has_many :through works when there are only two models. I know there are a whole bunch of answers, but none seem to give an example with only using two models, all other examples are using three+ example models.
The question I would like answered is why is it that whilst in the rails console I get two completely separate results with the commands a.friendships vs a.friends, e.g. why does a.friends know to return the users object back to me? but a.friendships does not.
#User.rb
class User < ApplicationRecord
has_many :friendships
has_many :friends, through: :friendships
end
#Friendship.rb
class Friendship < ApplicationRecord
belongs_to :user
belongs_to :friend, class_name: "User"
end
irb(main):020:0> a = User.first
irb(main):016:0> a.friendships
Friendship Load (0.1ms) SELECT "friendships".* FROM "friendships" WHERE "friendships"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Friendship id: 1, user_id: 1, friend_id: 2, created_at: "2019-06-10 20:27:16", updated_at: "2019-06-10 20:31:41">]>
irb(main):020:0> a = User.first
irb(main):019:0> a.friends
User Load (0.1ms) SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users"."id" = "friendships"."friend_id" WHERE "friendships"."user_id" = ? LIMIT ? [["user_id", 1], ["LIMIT", 11]]
=> #<ActiveRecord::Associations::CollectionProxy [#<User id: 2, email: "myemail#gmail.com", created_at: "2019-06-10 20:28:25", updated_at: "2019-06-10 20:28:25">]>
In designing a data model, you will sometimes find a model that should have a relation to itself. For example, you may want to store all employees in a single database model, but be able to trace relationships such as between manager and subordinates. This situation can be modeled with self-joining associations:
class Employee < ApplicationRecord
has_many :subordinates, class_name: "Employee",
foreign_key: "manager_id"
belongs_to :manager, class_name: "Employee"
end
With this setup, you can retrieve #employee.subordinates and #employee.manager.
Source: https://guides.rubyonrails.org/association_basics.html#self-joins
users table
-----------
-id
-...
friendships table
-----------------
- id
- user_id
- friend_id
When you call #user.friendships, the target is to find friendships of #user. it returns objects from friendships table, the middle table, which hold the relationships between users and users' friends
When you call #user.friends, the target is to find friends of the #user. It is to join users table and friendships table, where friendships.user_id = #user.id, then, it gets all friend_id of from those friendships records, and find users who has id included in that friend_ids array.

What is causing this error with my Active Record associations when I use model.collection.build?

In my project, I have the following three classes:
class User < ApplicationRecord
has_many :portfolios, dependent: :destroy
has_many :positions, through: :portfolios
end
class Portfolio < ApplicationRecord
belongs_to :user
has_many :positions, dependent: :destroy
end
class Position < ApplicationRecord
belongs_to :portfolio
end
When I try to build a position directly off the user model (user.positions.build(attributes)) by passing in an existing portfolio_id as one of the attributes, I get the following error:
ActiveRecord::HasManyThroughCantAssociateThroughHasOneOrManyReflection (Cannot modify association 'User#positions' because the source reflection class 'Position' is associated to 'Portfolio' via :has_many.
Why would this happen? I feel there's something to be learned here but I don't really get it!
Addendum: I think my associations make sense: a portfolio should only belong to one user, a position to only one portfolio, and a portfolio should have multiple positions and a user multiple portfolios.
You need to build positions like this
user = User.first
portfolio_attributes = {name: 'Portfolio_1'}
position_attributes = {name: 'Postion_1'}
user.portfolios.build(portfolio_attributes).positions.build(position_attributes)
user.save!
When i run user.positions i get the below result
user.positions
Position Load (0.6ms) SELECT "positions".* FROM "positions" INNER JOIN "portfolios" ON "positions"."portfolio_id" = "portfolios"."id" WHERE "portfolios"."user_id" = ? LIMIT ? [["user_id", nil], ["LIMIT", nil]]
=> #<ActiveRecord::Associations::CollectionProxy [#<Position id: 1, name: "Postion_1", portfolio_id: 1>]>

Rails has_many undefined method for association

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>]>

Rails has_many belongs_to foreign_key primary_key confusion

I was unable to setup my tables using the id field as the primary_id (I mean they are there but they don't mean anything to the business). ie Both the sales and customer tables come from our Microsoft SQL database.
In the Sales table I have a field called 'customer_id' that matches the 'customer_id' (not the 'id' field) in the customers table. My 'customer_id' field datatype is set to integer but in the migrations I never specified whether they were keys. I only indexed them.
The sale belongs to the customer and the customer has many sales so I am trying to make the has_many and belongs_to association.
sale.rb
class Sale < ActiveRecord::Base
belongs_to :customer, primary_key: "customer_id", foreign_key: "customer_id"
end
customer.rb
class Customer < ActiveRecord::Base
has_many :sales
end
However when I get the rails console and try to test this I am unable to have the SQL search for the customer_id, only the id from the customers table is searched.
rails console
2.0.0-p353 :001 > c = Customer.find_by_customer_id(400123)
Customer Load (2.0ms) SELECT "customers".* FROM "customers"
WHERE "customers"."customer_id" = 400123 LIMIT 1
=> #<Customer id: 16271, customer_id: 400123, customer_name: "ABC",
customer_street: "ABC", customer_suburb: "ABC", customer_state: "ABC",
customer_postcode: "ABC", dispatch_location_price_list: "ABC", customer_selling_price_list:
"ABC", customer_status: "ABC", line_of_business_id: "ABC", line_of_business: "ABC",
market_segment_id: "ABC", market_segment_name: "Other", customer_rep_id: 123, customer_rep_name: "ABC"
2.0.0-p353 :002 > c.sales
Sale Load (7.1ms) **SELECT "sales".* FROM "sales"
WHERE "sales"."customer_id" = $1 [["customer_id", 16271]]**
=> #<ActiveRecord::Associations::CollectionProxy []>
Specifically I just want the last bit to say [["customer_id', 400123]].
I know it is probably obvious but as I am still learning a lot of this stuff takes a bit to sink in (alot of cases I have answered my question just by asking it but not so tonight). Any suggestions would be greatly appreciated.
In my research I have found this in the API:
4.2.2.7 :primary_key
By convention, Rails assumes that the column used to hold the primary
key of this model is id. You can override this and explicitly specify
the primary key with the :primary_key option.
Which made me think I should be using the primary_key as the 'id' field isn't the field to be looked up using.
I also read this thread but I think it may be a little different than my situation.
Have you tried to add self.primary_key = :customer_id to the Customer model?

Problems with belongs_to condition in model

I'm making personal money management app. There I have 2 models: Account and Transaction.
Transaction has belongs_to :account and Account has has_many :transactions.
When I had account_id column in db (postgres) everything worked fine. But I have to rename account_id to from_account_id and add another column to_account_id. So that I can add a spending from Account and income to Account. Also it's necessary for transfers between accounts (where from_account_id and to_account_id both will have some values).
So, I've renamed fields :account_id in my views to :from_account_id and add :to_account_id field. But, when I rename account_id to from_account_id in db, I've got this error.
ActiveRecord::StatementInvalid in PagesController#index
PG::Error: ERROR: column transactions.account_id does not exist
LINE 1: ...s".* FROM "transactions" INNER JOIN "accounts" ON "transacti...
^
: SELECT "transactions".* FROM "transactions" INNER JOIN "accounts" ON
"transactions"."account_id" = "accounts"."id" WHERE "accounts"."user_id" = 17 ORDER BY
transactions.date DESC
My Pages#index controller is:
class PagesController < ApplicationController
def index
if user_signed_in?
#accounts = current_user.accounts.all
#transaction = current_user.transactions.build
#transactions = current_user.transactions.all
#categories = current_user.categories.all
end
end
end
I think Rails is trying to find account_id column in transactions table, because Transaction belongs_to :account. But I need both columns for :account to and from, as you see. So, maybe I need to change this belongs_to condition in Transaction model, but I don't know how.
Thanks for any ideas!
You have to define two belong_to relations in your Transaction class, one for each column in your table transactions:
class Transaction < ActiveRecord::Base
belongs_to :from_account, :class_name => 'Account', :foreign_key => 'from_account_id'
belongs_to :to_account, :class_name => 'Account', :foreign_key => 'to_account_id'
# ...
end
See options in Rails Relation Guide
Yo have to rename the has_many relation in Account model. Something like below will work:
has_many :transactions, :foreign_key => "from_account_id"

Resources