Rails (ActiveRecord) many to many table - ruby-on-rails

I have two models, Users and Groups. Each group can have many users and each user can be in many groups.
I currently have something simple like:
User:
has_many :groups
Group:
has_many :users
So I have a groups_users table which is just creating rows with group_id and user_id.
I want to add another column to this, (which I have), the question is how do I access it from a model without using a custom SQL call? In the group model I can go self.users and in user I can go self.groups
Is there a way to change the third column in this table from a user model?
Sorry if this is confusing, please advise on this

Here are a couple of tutorials that should help. Basically there two approaches to make many-to-many work, either has_and_belongs_to_many or has_many :through (recommended).
links:
http://blog.hasmanythrough.com/2006/4/20/many-to-many-dance-off
http://railscasts.com/episodes/47-two-many-to-many
http://railscasts.com/episodes/154-polymorphic-association

In Rails 3 you want to make a join table for many to many relationships, using the plural names of the tables you want to join in alphabetical order. So in this case it would be groups_users.
models
class GroupsUser < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :groups_users
has_many :groups, :through => :groups_users
end
class Group < ActiveRecord::Base
has_many :groups_users
has_many :users, :through => :groups_users
end

I [added] another column to [users_groups]...The question is how do
I access it from a model without using
a custom SQL call?
It sounds like you want to access a column of your user_groups table by calling a method on your User model or your Group model.
Some suggestions:
I'd name the table "user_groups" to work with ActiveRecord's pluralization expectations, but I'm not sure if that's essential.
Following Dave's advice, you'd want to set things up using the "has_many :through" technique...
# Declare a Model based on the many-to-many linking table.
class UserGroup < ActiveRecord::Base
belongs_to :user
belongs_to :group
end
class User < ActiveRecord::Base
has_many :user_groups
has_many :groups, :through => :user_groups
end
class Group < ActiveRecord::Base
has_many :user_groups
has_many :users, :through => :user_groups
end
Is there a way to change the third column in this table from a user model?
This is a little unclear, but keep in mind that each User can have a lot of UserGroups. So if you wanted to change that third column you'd have to find the particular one you're looking for.

Related

rails: accessing table from has_and_belongs_to_many relationship

ActiveRecord::Base.connection.tables
["groups", "groups_users", "members", "questions", "users", "votes", "schema_migrations"]
As you can tell, I have a table named, "groups_users" yet I cannot seem to access it. I can access the "users" table by User. I can access the "groups" table by Group. But how exactly do I access "groups_users"? I have tried:
Groups_Users
Groups_User
GroupsUsers
GroupsUser
I have also tried the singular version of Groups. Any ideas on how to access it? Documentation would also help.
Group
class Group < ActiveRecord::Base
has_and_belongs_to_many :users
end
User
class User < ActiveRecord::Base
has_and_belongs_to_many :groups
end
You have to define a model for groups_users table if you want to access it
class GroupUser < ActiveRecord::Base
self.table_name = "groups_users"
belongs_to :user
belongs_to :group
end
You can also define your associations like this
class Group < ActiveRecord::Base
has_many :group_users
has_many :users, :through => :group_users
end
class User< ActiveRecord::Base
has_many :group_users
has_many :groups, :through => :group_users
end
You can read about the difference here
Vimsha's answer is the right one; I'd also share my experiences with HABTM vs HMT for you:
HABTM is only for if you have "non-accessible" data. Rails' join
engine treats HABTM as having no model, and consequently you can't access the data directly. HMT (has_many :through) works by having a model & then allowing you access to it
The difference between HABTM & HMT is basically the direct access through the model. HABTM is there for "quick and dirty" collection access; HMT is there for more substancial data access
You need to create a model.
class GroupsUsers < ActiveRecord::Base
end

Double entry table in rails

I would like to create a double entry table form according two models.
For now I'm able to create a simple table with the members of a communities
on the columns, I must add the informations of an other model, like this :
My models :
Community
has_many :memberships
Membership
belongs_to :user
belongs_to :community
User
has_many ::memberships
has_many :skills
Skill
belongs_to :user
belongs_to :community
I there some gem existing to make a double entry table or is it easier to make it from scratch? if so, how can I begin ?
It seems like you would benefit from a through relationship here.
Instead of referencing community directly from the skill table, you could do:
Skill
belongs_to :user
has_many :communities, :through => :user
On user, add:
has_many :communities, :through => :memberships
Wouldn't this get the link between skill and community that you would like?
As Jay mentioned, you would benefit from a has_many :through relationship, or maybe a has_and_belongs_to_many relationship; whether it's the actual solution we'll have to see:
#app/models/user.rb
Class user < ActiveRecord::Base
has_many :memberships
has_many :skill_users
has_many :skills, through: :skill_users
end
#app/models/skill_user.rb
Class SkillUser < ActiveRecord::Base
belongs_to :skill
belongs_to :user
end
#app/models/skill.rb
Class Skill < ActiveRecord::Base
has_many :skill_users
has_many :users, through: :skill_users
end
This will allow you to associate each user (note that members are different than users) with specific skills without using double-entries in your tables
Relational
The basis of what you're seeking can be found in Relational Databases
These work by storing data in single instances, and linking to other data through foreign_keys. These foreign keys are things such as user_id etc:
(more information here)
This means instead of populating the same data twice, it is correct to reference that data from other models, as required. This is where join models come in
Join Model
Join models allow you to "link" two pieces of data through a join model:
For you, it means storing your skills in its own model, and linking users with skills on a join model (I've called skill_user.rb). This means that you'll be able to call your user's skills like this:
#user.skills #-> goes through the join model

Creating a many-to-many relation in Rails

So I'm Rails n00b and I want to create a "favorites" relationship such that a User can have many favorite Item. I'm not entirely sure how to do this, this is how I'm going to try but I'm not sure if this is a good practice at all:
class User < ActiveRecord::Base
has_many :favorites
//other code
end
class Favorite < ActiveRecord::Base
belong_to :user
has_one :item
end
class Item < ActiveRecord::Base
belongs_to :item
end
Is this a good way to do it? Should I be using has_and_belongs_to_many ?
I'm specially concerned in the following scenario: Say a user has 100 favorite items.
When I do a User.find(id) will I also be retrieving the 100 favorites and the 100 Items?
In case it's important: ruby version 1.9.3, rails version 3.2.11
Can you try has_many => :through?
class User < ActiveRecord::Base
has_many :favorites
has_many :items, :through => :favorites
//other code
end
In your case has_many :through is definitely the way to go. I would recommend reading: http://guides.rubyonrails.org/association_basics.html
Of particular interest with regard to your question:
2.8 Choosing Between has_many :through and has_and_belongs_to_many
Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use has_and_belongs_to_many, which allows you to make the association directly:
class Assembly < ActiveRecord::Base
has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model:
class Assembly < ActiveRecord::Base
has_many :manifests
has_many :parts, :through => :manifests
end
class Manifest < ActiveRecord::Base
belongs_to :assembly
belongs_to :part
end
class Part < ActiveRecord::Base
has_many :manifests
has_many :assemblies, :through => :manifests
end
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
It is better than using has_and_belongs_to_many.
When I do a User.find(id) will I also be retrieving the 100 favorites
and the 100 Items?
No. You'll just get the user object.
Update:
Calling User.include(:favourites, :items).find(id) will get you joined tables in case you want to make many calls to items table from user object.

Fetch data from third model in rails

I have such model structure:
class User < ActiveRecord::Base
has_many :groups, :through => :user_groups
has_many :orders
has_many :user_groups
end
-
class UserGroup < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
-
class Group < ActiveRecord::Base
has_many :user_groups
has_many :users, :through => :user_groups
end
In model group i have field markup.
How can i for every user via it's user_groups get group's markup field?
I try so:
user.user_groups.each do |u|
summ += u.groups.markup
end
Sure it is not working... But how to fetch data from third model?
user.groups.map(&:markup).sum should do it just fine
EDIT:
I used #flat_map because I was thinking that it was a nested array. But has_many :through would combine it into a single result list, so #map will be fine
EDIT2:
In discussion with #VladisAzamaris, it was pointed out that the markup column is a float, so sum is more appropriate than join
First off, may as well take advantage of that has_many :through here:
user.groups # => all the groups to which this user belongs
How about something like this to get the markup? This'll put 'em all in a list, unless you actually did want them all in one big string, in which case you'd join 'em.
user.groups.map(&:markup)
Also, if there aren't any other fields on the UserGroup model, consider a has_and_belongs_to_many relationship, in which Rails handles that glue UserGroup model for you rather than having you declare it.

rails, model naming question

I'm creating a model called Chats. And I want to assign users to a discussion. They are either a part of the Chats or they aren't...
So I create one model Chats.
What's the standard Rails naming convention for the other table?
ChatUsers?
While has_and_belongs_to_many is an ok option here, I recommend going with has_many :through instead.
In essence you will have an explicit join model, which you can call something like ChatSession.
class Chat < ActiveRecord::Base
has_many :chat_sessions
has_many :users, :through => :chat_sessions
end
class User < ActiveRecord::Base
has_many :chat_sessions
has_many :chats, :through => :chat_sessions
end
class ChatSession < ActiveRecord::Base
belongs_to :user
belongs_to :chat
end
Now you will need a table called chat_sessions with columns :user_id, and :chat_id in it. This is your join table.
Advantage
You get a model which is fully under your control, and isn't just a dumb join table managed by rails. So for example, if you want to track number of messages particular user left in particular chat, it could be a column in chat_sessions table. Presence of :through renders habtm unneeded in most cases. There is no complexity overhead either.
If it is a join table, it would be both table names joined by '_' and in alphabetical order of table names:
chats_users
This is called a has_and_belongs_to_many association in rails. You basically have two models that call has_and_belongs_to_many and create a linking table that uses the two models in the name (alphabetical and plural).
models:
class Chat < ActiveRecord::Base
has_and_belongs_to_many :users
end
class user < ActiveRecord::Base
has_and_belongs_to_many :chats
end
Then your tables would be
chats
users
chats_users

Resources