|
|||||||
New to SitePoint Forums? Register here for free!
|
![]() |
|
|
Thread Tools | Display Modes |
|
|
#1 |
|
SitePoint Member
Join Date: Oct 2005
Posts: 5
|
one table, many-to-many
I have a database where one table is users, users can know other users so that there will be a many to many relation of which users know what other users. ActiveRecord assumes the names in the join table is the table+_id. If I was gonna follow ActiveRecords naming this would be:
create table users_users( user_id int, user_id int, primary key(user_id, user_id) ); Now that obviously won't work, so how do I deal with this? |
|
|
|
|
|
#2 |
|
SitePoint Addict
![]() ![]() ![]() Join Date: Nov 2002
Location: Kharkov, Ukraine
Posts: 228
|
maybe you could add something like 'user_to_user' reference table with user_id and referenced_user_id fields to maintain many to many relationship
|
|
|
|
|
|
#3 |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
This should work (not tested it though):
Code:
CREATE TABLE users_known_users (
user_id INT,
known_user_id INT,
PRIMARY KEY(user_id, known_user_id)
);
class User < ActiveRecord::Base
has_and_belongs_to_many :known_users,
:join_table => 'users_known_users',
:foreign_key => 'user_id',
:association_foreign_key => 'known_user_id'
end
Example Usage: Code:
# Controller: @joe_bloggs = User.find(:name => 'Joe Bloggs') # View: <% @joe_bloggs.known_users.each do |known_user| %> Joe Bloggs knows <%= known_user.name %> <% end %> |
|
|
|
|
|
#4 |
|
SitePoint Member
Join Date: Oct 2005
Posts: 5
|
I'll try this, thanks very much
|
|
|
|
|
|
#5 |
|
SitePoint Wizard
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2001
Location: Atlanta, GA, USA
Posts: 5,059
|
I'd love to be proved wrong, but a month or so ago I barked up the this tree for hours and I'm totally convinced it cannot be done with has_and_belongs_to_many. It's a limitation of that option.
Still, I'd love to be proved wrong. The Agile Rails book suggests a combo of belongs_to and has_many on the same table, that may work for you. |
|
|
|
|
|
#6 | |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
Quote:
To the OP, did you try it? |
|
|
|
|
|
|
#7 | |
|
SitePoint Wizard
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2001
Location: Atlanta, GA, USA
Posts: 5,059
|
Quote:
If you are representing to me that that works, I would love to try it again (wouldn't be the first time I thought I incorrectly eliminated a possibility), but I have wasted so ... much ... time ... on this already, I can't bare to proactively test it without some assurance that it will work. |
|
|
|
|
|
|
#8 |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
Perhaps the OP could let us know.
|
|
|
|
|
|
#9 | |
|
SitePoint Addict
![]() ![]() ![]() Join Date: Jul 2002
Location: In the network.
Posts: 220
|
Quote:
First, I don't think you make a join table from a single table. So that's probably why you can't do it in ActiveRecord. By definition, you make a join table when you "join" two tables. If you want results from a single table you execute a query against that table. Second, if you are just working with a single table you do not have a many-to-many relationship. A many-to-many relationship is where you have 2 tables and one row in Table A is related to one or more rows in Table B AND one row in Table B is related to one or more rows in Table A. A one-to-anything relationship requires that there be more than one table. Otherwise you just have a "one" relationship, and that ain't no relationship at all! ![]() I think you should show us what the schema is for your users table. It honestly sounds like more of a DB design issue than a problem with ActiveRecord. |
|
|
|
|
|
|
#10 |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
OK, this is all rather tiring - I've already posted a solution yet people continue to say its "not possible". Its certainly possible and just to reaffirm things, I've knocked up quick Rails test app and with one small tweak to my originally posted code, it works. I see no reason why there cannot be a many-to-many relationship between objects of a single class, or in this case, records of a single table. I do not know if many-to-many is the correct term in this case - I know a one to * relationship within a single table is known as a recursive or reflexive relationship - I don't know if this would be called the same, but it makes sense either way.
You have a users table: Code:
CREATE TABLE users ( id INT, name VARCHAR(20), PRIMARY KEY(id) ); Code:
CREATE TABLE users_known_users ( user_id INT, known_user_id INT, PRIMARY KEY(user_id, known_user_id) ); Code:
luke = User.find(1) user.users Code:
class User < ActiveRecord::Base
has_and_belongs_to_many :users,
:join_table => 'users_known_users',
:foreign_key => 'user_id',
:association_foreign_key => 'known_user_id'
def known_users
self.users
end
end
Code:
luke = User.create(:name => 'Luke')
#=> #<User:0xb73d60b0 @attributes={"name"=>"Luke", "id"=>"1"}>
luke.known_users.length
#=> 0
luke.known_users.create(:name => 'Dave')
#=> #<User:0xb73be398 @new_record_before_save=true, @attributes={"name"=>"Dave", "id"=>2}, @new_record=false, @errors=#<ActiveRecord::Errors:0xb73bd858 @base=#<User:0xb73be398 ...>, @errors={}>>
luke.known_users.length
#=> 1
![]() |
|
|
|
|
|
#11 |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
OK, I've done a bit of further testing, and discovered a slight flaw with the above, which is...if you connect Person A to Person B, Person B should be connected to Person A as well. This works with a traditional many-to-many because each foreign key column in the join table points to a different table. However, in this case it doesn't, so it becomes neccesary to create two rows in the join table for each many-to-many join -- not ideal, but not a major hassle either. Its easily accomplished using two one-liner association callbacks. The updated model is now:
Code:
class User < ActiveRecord::Base
has_and_belongs_to_many :users,
:join_table => 'users_known_users',
:foreign_key => 'known_user_id',
:association_foreign_key => 'user_id',
:after_add => :create_reverse_association,
:after_remove => :remove_reverse_association
def known_users
self.users
end
private
def create_reverse_association(associated_user)
associated_user.known_users << self unless associated_user.known_users.include?(self)
end
def remove_reverse_association(associated_user)
associated_user.known_users.delete(self) if associated_user.known_users.include?(self)
end
end
|
|
|
|
|
|
#12 |
|
SitePoint Wizard
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2001
Location: Atlanta, GA, USA
Posts: 5,059
|
Hey, like I said, I'm happy! I wish I could find what I was doing that didn't work previously, I could have sworn it was exactly this, but obviously not.
A little note for anyone watching, (this confused me for a second), known_users.create does add a user to the database but it doesn't seem to commit the relationship to the database. Looks to me like you have to: Code:
>> luke.known_users.create(:name => 'Dave').save or >> dave = User.create(:name => 'Dave') >> luke.known_users << dave |
|
|
|
|
|
#13 |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
It seems that is the expected behaviour. You could also bypass the temp variable:
Code:
luke.known_users << User.create(:name => 'Dave') |
|
|
|
|
|
#14 | |
|
SitePoint Wizard
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2001
Location: Atlanta, GA, USA
Posts: 5,059
|
Quote:
|
|
|
|
|
|
|
#15 |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
Np
|
|
|
|
|
|
#16 |
|
SitePoint Member
Join Date: Oct 2005
Posts: 5
|
Luke's method works perfectly, thanks everyone for your great help
![]() |
|
|
|
|
|
#17 |
|
Non-Member
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Jan 2003
Posts: 5,799
|
Umm...
Seams to me, that what you've got to do - basically - is fart about all day, just to get this thing to work with the ActiveRecord? I'm sure there would - proberly - be a better approach to this no? |
|
|
|
|
|
#18 |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
What are you talking about? ActiveRecord works out of the box for no associations and with one or two lines of code for the common associations, and one or two lines of code for single table inheritance. And it took me all of an hour to work out how do do many to many relationships within the same table (hardly a common requirement).
ActiveRecord can also be set up to work with hierarchial structures using one or two lines of code. You hardly have to "fart about all day". What are you babbling on about? Do you even have a point, or something worthwhile to contribute to this forum? You may contribute to the PHP forum but it seems that all you do in here is ramble on about nothing you know little to nothing about. Why do you even bother? |
|
|
|
|
|
#19 | |
|
SitePoint Addict
![]() ![]() ![]() Join Date: Jul 2002
Location: In the network.
Posts: 220
|
Quote:
![]() BTW - Luke, didn't mean to imply by my earlier post that your solution wouldn't work. I'm still learning RoR, and in the original post it seemed as though the poster was going about things the wrong way. I guess as it turns out Rails uses slightly different terminology then I am used to. In the past I would have called the table users_known_users a "Linking Table" or a "Lookup Table". When I hear "Join Table" I think of a table created by executing a SQL JOIN statement, but I guess in RoR a "Join Table" == "Linking Table". Or maybe I have everything wrong. |
|
|
|
|
|
|
#20 | |
|
SitePoint Guru
![]() Join Date: Mar 2003
Location: London
Posts: 796
|
Quote:
|
|
|
|
|
|
|
#21 | |
|
Non-Member
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Jan 2003
Posts: 5,799
|
Quote:
![]() What am I babbling on about? Well, despite the manner in which I posted, what I was actually asking, is that is there not another way that you could tackle this, bar using the Active Record? I would have thought that was a bit obvious really, if you took the time not to read too much in the part about farting about all day ![]() There are alternatives, I'm just asking if they are appropriate that's all. As to me not knowing much about Ruby, well that is why I visit the forum in the first place, to learn more about it. Maybe I'll just not bother the next time, if that's your attitude huh? |
|
|
|
|
|
|
#22 | |||
|
SitePoint Addict
![]() ![]() ![]() Join Date: Jul 2002
Location: In the network.
Posts: 220
|
Quote:
If you are genuinely interested in learning more about Ruby / Rails (and not just trying to start fights) then something along the lines of : Quote:
Quote:
|
|||
|
|
|
|
|
#23 | |
|
SitePoint Wizard
![]() ![]() ![]() ![]() ![]() ![]() Join Date: Nov 2001
Location: Atlanta, GA, USA
Posts: 5,059
|
Quote:
You just chose habtm, enter the custom database details because you aren't doing the usual tableA to tableB thing and you are good to go. Really, it's like 2 lines of code to get the model working. |
|
|
|
|
|
|
#24 | |
|
SitePoint Member
Join Date: Jan 2006
Posts: 4
|
Quote:
Code:
class User < ActiveRecord::Base
has_and_belongs_to_many :friends,
:join_table => 'users_known_users',
:foreign_key => 'user_id',
:association_foreign_key => 'known_user_id',
:class_name => 'User'
end
|
|
|
|
|
|
|
#25 |
|
SitePoint Member
Join Date: Feb 2006
Posts: 1
|
I've gotten the user example above working. I've been trying to add a third field into the user_known_user table. I'm calling it "introduced_by_id". It is essentially stating which user introduced the two users together. I've added another has_and_belongs to many relationship called "introduced_by" with a similar layout to the known_users example. Whenever I try to call it however I always seem to retrieve the 2nd row in the table and not the third. Does anyone why this is happening?
Thanks! Last edited by Denyzine; Feb 23, 2006 at 15:25. Reason: Quesiton Update |
|
|
|
![]() |
| Bookmarks |
«
Previous Thread
|
Next Thread
»
| Thread Tools | |
| Display Modes | |
|
|
|
All times are GMT -7. The time now is 14:01.













Linear Mode
