has_many association with non-traditional data model - ruby-on-rails

I'm struggling with a has_many association. I have a diary application. The model players are as follows:
User
UserFriend
UserFoodProfile
I want to be able to get at all the foods that a user's friends have eaten. So, I want to be able to get: current_user.friends.profiles
I've setup the associations properly so far so that I'm able to access current_user.friends, but now I want to be able to get all the friend's entries as well over the last 30 days.
Here are my models
class User < ActiveRecord::Base
cattr_reader :per_page
##per_page = 20
has_many :user_food_profiles
has_many :preferred_profiles
has_many :food_profiles, :through => :user_food_profiles
has_many :weight_entries
has_one :notification
has_many :user_friends
has_many :friendships, :class_name => "UserFriend", :foreign_key => "friend_id"
has_many :friends, :through => :user_friends
class UserFriend < ActiveRecord::Base
belongs_to :user
belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
class UserFoodProfile < ActiveRecord::Base
belongs_to :user
belongs_to :food_profile
belongs_to :post
The UserFriend model is setup the following way:
id
user_id
friend_id
friend_name
I want to connect to user_food_profiles from friend so that I can get a user's friend's current user_food_profiles as "entries" but everything I've tried hasn't worked. How would I setup this association?
Tried to do:
UserFriend: has_many :user_food_profiles, :as => 'entries'
UserFoodProfile: belongs_to :friend, :foreign_key => 'friend_id'
Any ideas on how to make this work? Tempted to create a custom finder_sql but I'm sure this can work with associations.

Isn't a "friend" just another user that's in the database?
Let your UserFriend be a many_to_many relationship (either with "has_and_belongs_to_many" or "has_many :through"): each user can have several users as friends.
You can then link those user_ids (which could be in the many_to_many table called 'friend_id' if you like) to their foodprofile without a problem, since it is using the same link as user.foodprofile .

This is the line I see being the problem:
class User < ActiveRecord::Base
# <snip/>
has_many :friendships,
:class_name => "UserFriend",
:foreign_key => "friend_id"
I'm assuming that you're using a join table here called user_friend. That would mean that the foreign key there should be "user_id".
Now, unless you're going to store extra metadata in that UserFriend model, it's not required — you can get away with a self-referential has_and_belongs_to_many relationship like so:
has_and_belongs_to_many :friends,
:class_name => "User",
:join_table => "user_friends",
:foreign_key => "user_id",
:association_foreign_key => "friend_id"
Doing this, all you have to do is user.friends.profiles quite easily.
Now, if the relationship is bi-directional it gets a bit more complex, but I feel like this should at least get you started along the way.

Related

Rails has_many :through

I have a problem understanding the has_many and through relationship. I tried looking at a few other post here was not really clear.(Not sure if its not really because of what I am trying to achieve..)
I am trying to build and app where users have events in a calendar and each user can exchange events among each other.
I want to be able to retrieve data for each user with an API such as "user.trades" <-- which retrieve all trades that the user has made and the other methods, one "user.requested_trades" and the other "user.pending_trades". The requested_trades and pending_trades works but If I should make a 3 way table relations or this 2 way is good enough. Truth fully I really had to play around with the console to figure out what type of query it executes to be able to achieve the two methods to work. But its still very unclear the :through and :has_many.
class Trade < ActiveRecord::Base
belongs_to :seller,
:class_name => "User",
:foreign_key => "seller_id"
belongs_to :buyer,
:class_name => "User",
:foreign_key => "buyer_id"
end
class User < ActiveRecord::Base
has_many :events, :dependent => :destroy
has_many :requested_trades, -> { where(["trades.status = ?",'requested']).order("trades.created_at DESC") },
:class_name => "Trade",
:foreign_key => "buyer_id"
has_many :pending_trades, -> { where(["trades.status = ?",'pending']).order("trades.created_at DESC") },
:class_name => "Trade",
:foreign_key => "buyer_id"
has_many :sent_messages, -> { where(["messages.sender_deleted = ?", false]).order("messages.created_at DESC")},
:class_name => "Message",
:primary_key => "email",
:foreign_key => "sender_id"
has_many :received_messages, -> { where(["messages.recepient_deleted = ?", false]).order("messages.created_at DESC")},
:class_name => "Message",
:primary_key => "email",
:foreign_key => "recepient_id"
has_many :through is many to many relationship between two models. But this many to many relationship maintained through third model.
Suppose, two models teachers and departments. both are bi-directional(may-to-many relation).
But we can't able to maintain the relationship using only these two models so we need third model. Suppose, teacher_departments.
So as any department relation with teacher we can do using the making entry in 3rd model also vice versa.
class Teacher < ActiveRecord::Base
#It describes that teacher many-to many relationship with teacher_department model and
#also if teacher gets deleted depending entries in teacher_departments also gets deleted.
has_many :teacher_departments, dependent: :destroy
#It describe that teacher having multiple departments though 3rd teacher_departments
#model.
has_many :departments, through: :teacher_departments
end
class Department < ActiveRecord::Base
#It describes that many-to many relationship with teacher_department
#model and also if department gets deleted depending entries in teacher_departments
#also gets deleted.
has_many :teacher_departments, dependent: :destroy
#It describe that department having multiple teachers though 3rd teacher_departments
#model.
has_many :teachers, through: :teacher_departments
end
class TeacherDepartment < ActiveRecord::Base
#It describe that this model are belonging to both teacher and department model.
belongs_to :teacher
belongs_to :department
end
Example.
Teacher.first.departments.create()
It will create new entry in teacher_department table with teacher first id and newly created department id.
So you and access departments of first teacher by
Teacher.first.departments
You also take look at following link of Rails Guide
http://guides.rubyonrails.org/association_basics.html#choosing-between-has-many-through-and-has-and-belongs-to-many

Has many through relationship in rails

I am using facebook api to fetch users from fb.
I want to store the users in my model User
I am using has many through relationship to store users
User model relationship I have in my user model.
has_many :friends, :through => :user_friends, :class_name => "User", :foreign_key => "friend_id"
User friends model intermediate table to fetch friends of a user.
belongs_to :user
belongs_to :user, :foreign_key => "friend_id"
User friends has user_id and friend_id columns I added those in migration.
I get an error when I use .friends on a user object.
ActiveRecord::HasManyThroughAssociationNotFoundError: Could not find the association :user_friends in model User
can anyone help with this?
Thanks in advance.
You need to check Self-Referential Association. Apparently you are missing some concepts. You can not add 2 associations with the same name in a single model, (only one of them will respond).
You should add has_many :user_friends, but you will still be missing the other side of the association, check this example:
# user.rb
has_many :user_friends
has_many :friends, :through => :user_friends
has_many :inverse_friendships, :class_name => "UserFriend", :foreign_key => "friend_id"
has_many :inverse_friends, :through => :inverse_friendships, :source => :user
# user_friends.rb
belongs_to :user
belongs_to :friend, :class_name => "User"
Add has_many :user_friends before the has_many :through line
Try This:
User Model:
has_many :user_friends
has_many :friends, :through => :user_friends, :class_name => "User", :foreign_key => "friend_id"
Friends Model:
has_many :user_friends
has_many :users, through: :user_friends
User Friends Model
belongs_to :user
belongs_to :friend, :foreign_key => "friend_id"

Association through non-standard column?

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)

When an association is specified with class name, how do you access it? Rails 3

So lets I have a Transactions model.
Transactions.rb has:
belongs_to :buyer, :class_name => "User"
belongs_to :seller, :class_name => "User"
The user logged in, is accessed with current_user.
How can I do something like current_user.transactions? (which won't work as transactions doesn't have a user_id column) Or something like current_user.transactions.buyer?
Thanks!
(also the user model has_many :transactions)
All associations in Rails are one-way.
So for each belongs_to, you need to add a has_many or has_one on the other side.
I'd suggest you want something like this;
class User
has_many :transactions_as_buyer,
:class_name => "Transaction",
:foreign_key => :buyer_id
has_many :transactions_as_seller,
:class_name => "Transaction",
:foreign_key => :seller_id
end
Now you can say;
current_user.transactions_as_seller.map(&:buyer)
to get an array of buyers.
Of course, depending on your app you may have a better name than "transactions_as_buyer/seller"

Rails has many and belongs to one

I have a User model which has many projects and a Project model which can have many users, but also belongs to a single user (ie the user who created this project). It must belong to a User. It also allows a list of users to be associated with it, think collaboration.
With this in mind, my models look like this:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
end
class AssignedProject < ActiveRecord::Base
belongs_to :user
belongs_to :project
end
Now, when I want to create a new project through a User, this is how I would do it:
user = User.create(:name => 'injekt')
user.projects.create(:name => 'project one')
Now, I know that projects is provided through an AssignedProject join model, which is why project.user will return nil. What I'm struggling to get my head around is the best way to assign the project creator (which by the way doesn't need to be user, it could be creator or something else descriptive, as long as it is of type User).
The idea then is to create a method to return projects_created from a User which will select only projects created by this user. Where user.projects will of course return ALL projects a user is associated with.
Assuming this kind of association is fairly common, what's the best way to achieve what I want? Any direction is greatly appreciated.
Add a creator_id column to your projects table for the creator relationship, and then add the associations to the models:
class User < ActiveRecord::Base
has_many :assigned_projects
has_many :projects, :through => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
belongs_to :user
has_many :assigned_projects
has_many :users, :through => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end
http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html#method-i-has_many
I wanted to add little improvement to design. We don't actually need intermediate model because it does not contain any extra column other than reference_ids hence HABTM association is best suited over here.
class User < ActiveRecord::Base
has_and_belongs_to_many :projects, :join_table => :assigned_projects
has_many :created_projects, :class_name => "Project", :foreign_key => :creator_id
end
class Project < ActiveRecord::Base
has_and_belongs_to_many :users, :join_table => :assigned_projects
belongs_to :creator, :class_name => "User", :foreign_key => :creator_id
end

Resources