Rails has_many, belongs_to using three tables - ruby-on-rails

I have three tables
Memberships
User_Groupings
Users
This is how I am trying to join them.
User.id == Memberships.user_id
User.user_type == User_Groupings.user_type
Memberships.group_id == User_Groupings.group_id
I would like to do this by using associations if possible.
I have no idea how to do a belongs to or has many that is this complex.
My end goal is to be able to call user_grouping.users and get all users that match the above fields.

visit http://guides.rubyonrails.org/association_basics.html
it will solve all your question related to associations .

User.id == Memberships.user_id
gives:
class Membership
belongs_to :user
class User
has_many :memberships
but the other two are not standard Rails associations.
User.user_type == User_Groupings.user_type
Memberships.group_id == User_Groupings.group_id
You could potentially model these by passing conditions to the associations
perhaps you could update the names of these tow reflect more standard associations (or at least to give us a better idea what you want)
eg
User.user_grouping_id == User_Groupings.id
Memberships.user_grouping_id == User_Groupings.id
then if you want "user_type" you can make it a method eg:
class User
def user_type
self.user_grouping.user_type
end

I may be not quite seeing what you are doing and therefore missing the point.
It seems you are trying to do too much with user_groupings in that it has an ID field fir user_type and group_id. My first reaction would therefore be to say that you should have user_type and group_id to either be in two different tables, or to have them as the same field (the second option seems unlikely)
You have none standard foreign keys by using belongs_to :user, :foreign_key => :xxxxx_id
sorry this isn't directly answering your question, but I think you should take another look at your database design

Looks like you need to have another model to associate memberships and users.
And then you associations will be like this.
class User
belongs_to :user_grouping
has_many :user_memberships, :dependent => :destroy
has_many :memberships, :through => :user_memberships
end
And..
class UserGrouping
has_many :users, :dependent => :nullify
end
And..
class Membership
has_many :user_memberships, :dependent => :destroy
has_many :users, :through => :user_memberships
end
And..
class UserMembership
belongs_to :user
belongs_to :membership
end
Of course, you have to puts respective foreign keys in tables.
Hopefully, this will help you.

Related

rails join table issue with different roles (owner, non-owner)

In my app users can create products so at the moment User has_many :products and Product belongs_to :user. Now I want the product creator product.user to be able to invite other users to join the product, but I wanna keep the creator the only one who can edit the product.
One of the setups I've got in my mind is this, but I guess it wouldn't work, since I don't know how to distinguish between created and "joined-by-invitation" products when calling user.products.
User
has_many :products, through: :product_membership
has_many :product_memberships
has_many :products # this is the line I currently have but think it wouldn't
# work with the new setup
Product
has_many :users, through: :product_membership
has_many :product_memberships
belongs_to :user # I also have this currently but I'd keep the user_id on the product
# table so I could call product.user and get the creator.
ProductUsers
belongs_to :user
belongs_to :product
Invitation
belongs_to :product
belongs_to :sender, class: "User"
belongs_to :recipient, class: "User"
To work around this issue I can think of 2 solutions:
Getting rid of the User has_many :products line that I currently have and simply adding an instance method to the user model:
def owned_products
Product.where("user_id = ?", self.id)
end
My problem with this that I guess it doesn't follow the convention.
Getting rid of the User has_many :products line that I currently have and adding a boolean column to the 'ProductUsers' called is_owner?. I haven't tried this before so I'm not sure how this would work out.
What is the best solution to solve this issue? If none of these then pls let me know what you recommend. I don't wanna run into some issues later on because of my db schema is screwed up.
You could add an admin or creator attribute to the ProductUsers table, and set it to false by default, and set it to true for the creator.
EDIT: this is what you called is_owner?
This seems to be a fairly good solution to me, and would easily allow you to find the creator.
product.product_memberships.where(is_owner?: true)
should give you the creator

How do I sum a many to many value?

Each User can have many Resources, and each of those Resources has many Votes, and each of those votes have a value attribute that I want to sum all that particular users resources.
If I were to type this in a syntactically incorrect way I want something like...
#user.resources.votes.sum(&:value), but that obviously won't work.
I believe I need to use collect but I am not sure?
This is the closest I got but it prints them out, heh
<%= #user.resources.collect { |r| r.votes.sum(&:value) } %>
I'd recommend setting up a has_many :through relationship between the User and Vote objects. Set the models up like this:
class User < ActiveRecord::Base
has_many :resources
has_many :votes, :through => :resources
end
class Resource < ActiveRecord::Base
belongs_to :user
has_many :votes
end
class Vote < ActiveRecord::Base
belongs_to :resource
end
Once this is done you can simply call user.votes and do whatever you want with that collection.
For more info on has_many :through relations, see this guide: http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
How can you tell who voted having a Vote instance? Your Vote model has to have voter_id field and additional association:
# in Vote.rb
belongs_to :voter, class_name: 'User', foreign_key: 'voter_id'
And in your User model:
# in User.rb
has_may :submited_votes, class_name: 'Vote', foreign_key: 'voter_id'
So, #user.votes (as David Underwood proposed) will give you #user resources' votes. And #user.submited_votes will give you votes submitted by the #user.
Using just User <- Resource <- Vote relation won't allow you to separate some user's votes made by him and votes made for its resources.
For a total sum this should work or something real close.
sum = 0
#user.resources.each do |r|
r.votes.each do |v|
sum += v.value
end
end
This might work for you:
#user.resources.map {|r| r.votes.sum(:value)}.sum
How many records do you have, there is a way to push this to the database level I believe, I would have to check, but if it is only a few records then doing this in ruby would probably be ok
Try this code
#user.resources.map(&:votes).flatten.map(&:value).sum

Has_many: through in Rails. Can I have two foreign keys?

I am a rails newbie and I am trying to create a database schema that looks like the following:
There are many matches. Each match has 2 teams.
A team has many matches.
The team model and match model are joined together through a competition table.
I have that competition model with a match_id and a team1_id and a team2_id.
But I don't know how to make this work or if it's even the best way to go about it. I don't know how to make certain teams team1 and others team2.... two foreign keys? Is that possible?
The match table also needs to hold additional data like team1_points and team2_points, winner and loser, etc.
You can have as many foreign keys as you want in a table. I wrote an application that involved scheduling teams playing in games.
The way that I handled this in the Game class with the following:
class Game < ActiveRecord::Base
belongs_to :home_team, :class_name => 'Team', :foreign_key => 'team1_id'
belongs_to :visitor_team, :class_name => 'Team', :foreign_key => 'team2_id'
You can add appropriate fields for team1_points, team2_points, etc. You'll need to set up your Team model with something like:
class Team < ActiveRecord::Base
has_many :home_games, :class_name => 'Game', :foreign_key => 'team1_id'
has_many :visitor_games, :class_name => 'Game', :foreign_key => 'team2_id'
def games
home_games + visitor_games
end
#important other logic missing
end
Note that some of my naming conventions were the result of having to work with a legacy database.
I faced a similar problem, and extending the previous answer, what I did was:
class Game < ActiveRecord::Base
def self.played_by(team)
where('team1_id = ? OR team2_id = ?', team.id, team.id)
end
end
class Team < ActiveRecord::Base
def games
#games ||= Game.played_by(self)
end
end
This way, Team#games returns an ActiveRecord::Relation instead of an Array, so you can keep chaining other scopes.

has_one :through => multiple

Both Attendment & Vouching:
belongs_to :event
belongs_to :account
Therefore: 1 to 1 relationship between attendments and vouchings.
Is there a way to do this without my thinking too much?
# attendment
has_one :vouching :through => [:event, :account]
Note: I don't mind thinking too much, actually.
Yeah i don't think you can use a has_one for this. Assuming I'm reading this correctly, you have two models:
Attendment
Vouching
They both store an event_id and account_id. You want to know from the attendment model, what vouching shares the same event and account as the attendment. I think the easiest solution for this is to write a method inside your attendment.rb file.
class Attendment < ActiveRecord::Base
# belong to statements go here
def voucher
Voucher.where(:event_id => self.event_id, :account_id => self.account_id).first
end
end

Rails using a join model attribute in a condition for a find

I'm using a :has_many, :through association to link two models, User and Place
It looks like this -
In User:
has_many :user_places
has_many :places, :through=>:user_places
In Place:
has_many :user_places
has_many :users, :through=>:user_places
In User_Place
belongs_to :user
belongs_to :place
belongs_to :place_status
On that last one note the place_status.
I want to write a find that returns all places associated to a user with a particular place_status_id.
Place_Status_id is on the join model, user_place.
So basically I want
User.places.where(:place_status_id=>1)
(in rails 3)
but i get an error with that because place_status_id isnt on the place model.
Any ideas? Thanks all.
I believe you can do your find this way
#user.places.joins(:user_places).where(:user_places => {:place_status_id => 1})
I've never used Rails 3, so I'm sorry if there's any errors.

Resources