I'm working on Ruby on rails 2.3.8, and I'd like to build a functionality to follow users.
For this purpose, I've created 2 tables: users and followings, with their respective models:
class User < ActiveRecord::Base
has_and_belongs_to_many :followings, :foreign_key => "follower_id"
end
class Following < ActiveRecord::Base
has_and_belongs_to_many :users, :foreign_key => "follower_id", :class_name => "User"
end
Now, when I try to execute current_user.followings.all (with a valid current_user, of course), it throws me the following exception:
'followings_users' doesn't exist: SELECT 'followings'.* FROM `followings` INNER JOIN 'followings_users' ON 'followings'.id = 'followings_users'.following_id WHERE ('followings_users'.follower_id = 1 )
I can't make this work. I don't know why it asks me for a "followings_users" table. What if I want to call it just "followings"?
What am I doing wrong here?
When using habtm, Rails will try to get the associations from a join table. It tries to guess the name of the table by joining the two names (followings_users), or you can specify the name of the table as an option. But you have to explicitely create this table in your migrations.
See documentation here
You can also do this using the newer syntax has_many :through:
class User < ActiveRecord::Base
has_many :user_followings
has_many :followings, :through => :user_followings
end
class UserFollowing < ActiveRecord::Base
belongs_to :users
belongs_to :followings
end
class Following < ActiveRecord::Base
end
This requires a join table called :user_followings with columns user_id and following_id.
The new syntax is generally preferred over HABTM because it allows you to define methods on the join model, which can be useful, although HABTM still works fine as well.
Related
I'm able to successfully create a through table using the naming convention of friends and friend. However I'd like to use connections instead of friends.
Active record throws the following error when I try to use connections: PG::UndefinedColumn: ERROR: column medical_relationships.connection_id does not exist
class User < AR::Base
has_many: :medical_relationships
has_many: :friends, through: :medical_relationships
# I'd like to use
# has_many: :connections, through: :medical_relationships
class MedicalRelationship < AR::Base
belongs_to :user
belongs_to :friend, :class_name => "User"
# belongs_to :connection, :class_name => "User"
this seems to be a singular and plural naming convention issue in rails but I'm not sure.
That id does not exist on the medical_relationships table. You can either add :foreign_key => :user_id (assuming that is your user table's primary key) or change the name of the column in the table. I would advise against the second.
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
I have three Models setup with the following associations
class User < ActiveRecord::Base
has_many :faculties
has_many :schools, :through => :faculties
end
class School < ActiveRecord::Base
has_many :faculties
has_many :users, :through => :faculties
end
class Faculty < ActiveRecord::Base
belongs_to :user
belongs_to :school
end
and in my controller i go to create a school and assign the user
class SchoolsController < ApplicationController
def create
#school = current_user.schools.build(params[:school])
...
end
end
When I login and submit the form the flash displays success, but the association doesn't build on the join table.
I tried it inside the apps console and it builds the association just fine.
I've been stuck on this for a couple days now and I just cannot figure out what I am missing. Thank in advance for any and all advice
The build method does not save the object. You need to explicitly call #school.save.
Two things: If the schools association is :through a has_many association, you will have to select which parent the School exists through.
So, for instance, if you were to nest School resources under users as in /users/:id/faculties/:id you could create a school via current_user.faculties.find(params[:faculty_id]).schools.build(params[:school]).save
Based on the example code, it looks like the fundamental problem is that the has_many xxx, :through syntax is being used without specifying the id of the faculties record. Remember two things: 1) ActiveRecord doesn't natively support composite primary keys, and 2) you must call #save on associated records created using #build. If you remember these, you should be fine.
I have the following model:
class Advisor < ActiveRecord::Base
belongs_to :course
end
class Course < ActiveRecord::Base
has_many :advisors
has_many :sessions
has_many :materials, :through=>:sessions
end
class Session < ActiveRecord::Base
belongs_to :course
has_many :materials
end
class Material < ActiveRecord::Base
belongs_to :session
end
I.e., every advisor teaches one course, every course has sessions, and every session has materials.
I want to traverse from an advisor to all the associated materials, i.e. something like: Advisor.first.materials
I tried to do:
class Advisor < ActiveRecord::Base
belongs_to :course
has_many :sessions, :through=>:course
has_many :materials, :through=>:sessions
end
But it didn't work as it treated sessions as a many-to-many table: Unknown column 'sessions.advisor_id' in 'where clause': SELECT 'material'.* FROM 'materials' INNER JOIN 'sessions' ON 'materials'.session_id = 'sessions'.id WHERE (('sessions'.advisor_id = 1))
I then tried to do:
class Advisor < ActiveRecord::Base
belongs_to :course
has_many :materials, :through=>:course
end
In an attempt to have the association use the "materials" association in the "Course" model, but received:
ActiveRecord::HasManyThroughSourceAssociationMacroError: Invalid source reflection on macro :has_many :through for has_many :materials, :through=>:sessions. Use :source to specify the source reflection.
Tried to use "sessions" as the source which was a nice try but made me receive only the sessions rather than the materials.
Any ideas if this is at all possible?
I'm using Rails 2.3.8 (perhaps time to upgrade?)
Thanks!
Amit
I want to traverse from an advisor to
all the associated materials
Unless I'm missing something, using the associations specified in your first example, you can simply call materials on the associated course:
a = Advisor.first
materials = a.course.materials
Using a has_many,through association for another has_many,:through relation will not work in rails
instead of creating a association you can just create a method to access all the materials by a Advisor
def materials
sessions.collect(&:materials).flatten
end
This will work, but you wont be able to chain find queries to this method. If you want to be able to chain find methods something like #advisor.materials.find.. Then inside this method use Material.find() with appropriate conditions
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.