Association through non-standard column? - ruby-on-rails

I have a Friends model with user_id and friend_id...both are id's back to specific users.
What I'd like to able to do is something like this:
#relationship = Friend.find_by_user_id(current_user.id)
#relationship.friend.username
Where I can basically pull the user through the friend_id column in the same way I can do #relationship.user.username.
How would I setup my associations to pull that off?

Isn't this just a many-to-many situation.
I think there is a perfect solution in Many-to-many relationship with the same model in rails?
Hope this help!

Use class_name if your column doesn't reflect the convention expected:
class Friendship < ActiveRecord::Base
belongs_to :user
belongs_to :friend, class_name: 'User'
end
Note that I modified the model name to Friendship to better reflect the use of this model. Also, you can make it easier for yourself if you revise your User model to look like this:
class User < ActiveRecord::Base
has_many :friendships
has_many :friends, :through => :friendships
has_many :friendships_of, :class_name => 'Friendship', :foreign_key => :friend_id
has_many :friends_of, :through => :friendships_of, :source => :user
end
Now to see all the user's friends:
current_user.friends.map(&:username)
And to see who has 'friended' the user:
current_user.friends_of.map(&:username)

Related

Rails many to many relationship confusion

So im working on a rails app for users to create events (and attend other created events). You can read about the assignment here (for the Odin Project): https://www.theodinproject.com/courses/ruby-on-rails/lessons/associations
Anyways I thought I had understood many to many relationships in rails, but the way i've seen other people write the models is confusing to me.
To me it seems like it should be something like:
class User < ApplicationRecord
has_many :attendances
has_many :events, through: :attendances
end
class Attendance < ApplicationRecord
belongs_to :user
belongs_to :event
end
class Event < ApplicationRecord
has_many :users
has_many :users, through: :attendances
end
This makes sense to me because a User can create many events, and an event can have many users attending. (Although attendances is probably the wrong word, maybe invites or something).
But i've seen some weird examples (You can see others source code below on the project) and it seems like they are adding much more to the models and also renaming the source/foreign_key/class_name.
Am I missing something? This still allows a user to "own" an event right? Maybe im mis-understanding how many-to-many works. But this fits at least in my mind of how it should be.
For reference some other models I was seeing was similar to this:
class Event < ActiveRecord::Base
belongs_to :creator, :class_name => "User"
has_many :event_attendees, :foreign_key => :attended_event_id
has_many :attendees, :through => :event_attendees
end
class EventAttendee < ActiveRecord::Base
belongs_to :attendee, :class_name => "User"
belongs_to :attended_event, :class_name => "Event"
end
class User < ActiveRecord::Base
has_many :created_events, :foreign_key => :creator_id, :class_name => "Event"
has_many :event_attendees, :foreign_key => :attendee_id
has_many :attended_events, :through => :event_attendees, :foreign_key => :attendee_id'
end
Basically similar things to the above. Im not really sure what this is doing? Or why all the extra is necessary.
In your example everything according to conventions. Maybe except many-to-many table naming.
attendances table has 'user_id' and 'event_id' fields. But in case it could conflict with other fields, or not descriptive enough you could use different keys.
belongs_to :creator, :class_name => "User"
belongs_to :creator by default would look for Creator model, so it is needed to specify class name explicitly, like in the provided example.
has_many :event_attendees, :foreign_key => :attended_event_id
By default foreign key would be event_id, so here it is specified explicitly too.
has_many :created_events, :foreign_key => :creator_id, :class_name => "Event"
By default, rails would look for user_id foreign key and CreatedEvent model. And these attributes specified explicitly.
You just need to understand what attributes rails provides by default, to change if it is required.
ActiveRecord associations default to a class and foreign key with the same name as the association. The code here is specifically specifying these because they are not the default.

How to associate through a join table that reference the same model?

I have a model called House and I want to be able to associate houses with each other to show recommendations.
So I would expect that given a house, I should be able to ask: house.recommended_houses. A house could be recommended for more than one house.
I was thinking on having a table that would store this association (I don't know the name yet), so it would have the following columns:
recommended_house_id
recommended_for_house_id
I am failing to understand how would I hook this up with my House model. What would the associations look like, and also what name should I be using for that join model?
This should get you started:
class House < ApplicationRecord
has_and_belongs_to_many :recommendations,
class_name: "House",
foreign_key: "recommended_by_id",
association_foreign_key: "recommendation_id"
end
What you're describing is called a self-referential association.
You can set up a join table (recommendations) and the associated model:
class Recommendation < ActiveRecord::Base
belongs_to :house
belongs_to :recommended_house, :class_name => 'House'
end
and then use has_many, :through relationships within the House model to set up the relationships you're looking for.
class House < ActiveRecord::Base
has_many :recommendations
has_many :recommended_houses, :through => :recommendations
has_many :inverse_recommendations, :class_name => "Recommendation", :foreign_key => "recommended_house_id"
has_many :recommended_by_houses, :through => :inverse_recommendations, :source => :house
end
Now you can use both house.recommended_houses and house.recommended_by_houses.

has_many :through relationships explained

I'm new to Rails and have some doubts about the kind of relationship do I need to use. Here is the case.
I have two models Offer and User, a user could belong to to many offers and offers can have many user. Also the users create the offers.
I think I have to use a has_many :through ralationship. For example I've created another model "Applicant". Applicant belongs_to user and belongs_to offer. But how is the relationship from the user and offer model? For example:
User Model
has_many :offer, :through => :applicant
Offer Model
has_many :user, :through => :applicant
My doubt is because I already have this two relationship
User Model
has_many :offers, :dependent => :destroy
Offer Model
belongs_to :user
After solve this, I guest I have to save the record in the applicant model from the applicanst_controller, right?
Thanks in advance
What you have described is a many-to-many relationship using a join table. You're actually pretty close but you just need to remove the has_many :offers, :dependent => :destroy from your user model and the blongs_to :user in your offer model. It should look something like this:
class User < ActiveRecord::Base
has_many :offers, :through => :applicants
end
class Applicant < ActiveRecord::Base
belongs_to :users
belongs_to :offers
end
class Offer < ActiveRecord::Base
has_many :users, :through => :applicants
end
You don't have to worry about the dependent destroy part as associations are automatically removed as the corresponding objects are removed. With a many to many association it doesn't really matter how you go about building the relationship. Either of the following will work:
#user.offers << #offer
#offers.users << #user
If you don't need to store any information specific to your applicant join table (e.g., time stamps, descriptions) you might instead want to look at a has_and_belongs_to_many relationship. Check out choosing between has_many_through and has_and_belongs_to_many for reference.
Edit
Heres the code for a HABTM relationship:
class User < ActiveRecord::Base
has_and_belongs_to_many :offers
end
class Offer < ActiveRecord::Base
has_and_belongs_to_many :users
end

How to access referenced table in a n:m relation in Ruby on Rails?

I have got a Database like this:
users
-id
user_cars
-user_id
-car_id
cars
-id
-model
-color
So a user can have multiple cars, and detailed information about the cars are in a big cars table. I also created the models with the relationships.
class User
has_many :user_cars
class User_car
belongs_to :user
belongs_to :cars
class Car
has_many :user_cars
Now I want to access the information for all the cars for one user. My first approach would be to get at least one information (i.e. color) from the cars table.
I tried this one, just as an example for accessing the middle table:
#user_id = current_user.user_cars.find(1).user_id
works! But when I try to access the cars table I always get an error.
#color = current_user.user_cars.cars.find(1).color
undefined method `cars' for #<ActiveRecord::Relation:0xaf92e8c>
So I think Im doin something easy very wrong...
When I know how to get access to the third table, I have to do it in that fashion, that I only get results for the user and not just only the first entry, maybe you guys can help me with that aswell. Thanks!
The issue in your example by the way is that belongs_to should be singular. Also, your order was wrong.
#color = current_user.user_cars.find_by_car_id(1).car.color
You should rewrite this to use a has_many through association:
class User
has_many :user_cars
has_many :cars, :through => :user_cars
class UserCar
belongs_to :user
belongs_to :car
You can then access the cars by doing:
current_user.cars
And the color by doing:
#color = current_user.cars.find_by_car_id(1).color
EDIT
After some debugging, it turns out that the Car model has a class property. Class is a reserved word in ruby. Be careful with naming your attributes!
Without has_many :through associations:
#color = current_user.user_cars.where(:car_id => 1).first.color
With them:
class User < ActiveRecord::Base
has_many :user_cars, :foreign_key => :user_id, :class_name => "UserCar", :inverse_of => :user
has_many :cars, :through => :user_cars
end
class UserCar < ActiveRecord::Base
belongs_to :user
belongs_to :car
end
class Car < ActiveRecord::Base
has_many :user_cars, :foreign_key => :car_id, :class_name => "UserCar", :inverse_of => :car
has_many :cars, :through => :user_cars
end
#color = current_user.cars.find(1).color
http://guides.rubyonrails.org/association_basics.html#the-has_many-through-association
:through defines a shortcut
:inverse_of defines a method (association) which represents current model in :class_name model
:class_name defines which model should be represented by :user_cars
:foreign_key tells which column in the target model's table represents current model (in table user_cars (or users_cars, depends on how you define the association, i think it should be user_cars in this example.. and users_cars for has_and_belongs_to_many)
Details on those are in the link above.

Relationship like Twitter followers/followed in ActiveRecord

I'm trying to represent a relationship between users in my application where a user can have many followers and can follow other users.
I would like to have something like user.followers() and user.followed_by()
Could you please tell me in details how to represent this using ActiveRecord?
Thanks.
You need two models, a Person and a Followings
rails generate model Person name:string
rails generate model Followings person_id:integer follower_id:integer blocked:boolean
and the following code in the models
class Person < ActiveRecord::Base
has_many :followers, :class_name => 'Followings', :foreign_key => 'person_id'
has_many :following, :class_name => 'Followings', :foreign_key => 'follower_id'
end
and corresponding in the Followings class you write
class Followings < ActiveRecord::Base
belongs_to :person
belongs_to :follower, :class_name => 'Person'
end
You could make the names clearer to your liking (i especially don't like the Followings-name), but this should get you started.
The twitter follower model differs from the friendship model in that you do not need permission from the person to follow them. Here I set up a lazy loading where the relationship is not fully defined in the person object.
/app/models/person.rb
def followers
Followership.where(:leader_id=>self.id).not_blocked
end
def following
Followership.where(:follower_id=>:self.id).not_blocked
end
has_many :followers, :class_name=>'Followership'
has_many :followed_by, :class_name=>'Followership'
/app/models/followership.rb
belongs_to :leader, :class_name=>'Person'
belongs_to :follower, :class_name=>'Person'
#leader_id integer
#follower_id integer
#blocked boolean
scope :not_blocked, :conditions => ['blocked = ?', false]

Resources