Ruby on Rails: has_many referential --which model objects does it own? - ruby-on-rails-3

I am new to Rails and finished Michael Hartl's "Ruby on Rails 3 Tutorial". Although the book teaches me a lot, I find this puzzle I don't understand.
To preview the puzzle, that is, I don't understand, inside User model,
has_many :following, :through=>:relationship, :source=>:followed
how this piece of code link "user.following" to an array of User instances.
And below is the whole puzzle.
First of all, I have the Relationship model, which records followed_id and follower_id infos. Inside Relationship model, the association is simple as
class Relationship < ActiveRecord::Base
attr_accessible :followed_id
belongs_to :follower, :class_name => "User"
belongs_to :followed, :class_name => "User"
end
Then, inside the User model, a user will assume the role of follower, and collect all its following rows in relationships table through relationships association.
class User < ActiveRecord::Base
.
.
.
has_many :relationships, :foreign_key => "follower_id", :dependent => :destroy
.
Until now, I got it.
But confusion came at the next line, where through user.following it can assemble all that user's following(User instances). Like so,
has_many :following, :through=>:relationships, :source=>:followed
I understand that :source=>:followed will overwrite the default, and let find all followed_ids associated with that user.
But, how can Rails recognize followed_id to link to User object? The label name doesn't match users, nor is there :class_name specified. I just don't get how Rails do this underlying work, or I missed out some hints.
Thank you! :)

But, how can Rails recognize followed_id to link to User object? The
label name doesn't match users, nor is there :class_name specified. I
just don't get how Rails do this underlying work, or I missed out some
hints.
Rails recognize that is an user object because it is set in Relationship's belongs_to. What Rails does here is to follow the relationship class through the foreign key "follower_id" and returning every User that has a relationship with the current user as followed. Of course Rails do that in a single SQL statement like this:
SELECT `users`.* FROM `users` INNER JOIN `relationships` ON `relationships`.followed_id = `users`.id WHERE ((`relationships`.follower_id = <the current user id> ))

has_many :following, :through=>:relationships, :source=>:followed
This explains to Rails that following is the inverse relationship of following and that users has many following and followed through his relationships.
The way Rails knows that followed_id is linked to User is that it is defined in your Relationship model.
Hope you've understood ! Good luck :)

Related

rails multiple has_many relationships between the same models

I'm trying to figure out the best way to model a many-to-one relationship in rails where there are multiple scopes to the relationship.
An example would be a restaurant has-many photos. I want to be able to call
restaurant.lounge_photos
and receive only the lounge photos,
but also be able to call
restaurant.food_photos
and receive just the food photos.
The two methods I can think of are:
to use multiple joins table, and a has_many to has_one relationship for each type of photo.
to add a 'type' attribute to the photo model and write a scoping method.
Both of these seem a bit clunky to me.
Is there a better solution here?
I think you have to go has_many and Single Table Inheritance(STI), as follow.
Make association with restaurant and photo
class Restaurant < ActiveRecord::Base
has_many :photos
end
class Photo < ActiveRecord::Base
belongs_to :restaurant
end
Then you have to use STI in Photo model. The reason is that you have almost all fields are common for lounge_photos and food_photos.
OR
Using scope directly you can differentiate it and achieve your goal.
For more details of use STI you can refer this link.
This is one way, using a type column
has_many :food_photos,
class_name: 'Photo',
foreign_key: :restaurant_id,
-> { where(type: 'food') }
has_many :lounge_photos,
class_name: 'Photo',
foreign_key: :restaurant_id,
-> { where(type: 'lounge') }

Extra Role column in join table in Rails

I have users and companies in a many to many relationship by a join table which has a column for user Role. I'm not sure if this is the best way to have the model set up.
Now each user can have different roles depending on the company, what is the best way to design and access user's role using ActiveRecord associations?
I would like to return via JSON the user's role based on their current company and default to something if their company is nil OR their role has not been set (nil).
Update:
What I've got now after reading Many-to-many relationship with the same model in rails? which is a bit different (many to many on itself).
CompaniesUser
belongs_to :company
belongs_to :user
Company
has_many(:companies_users, :dependent => :destroy)
has_many :users, :through => :companies_users
User
has_one :company
has_many(:companies_users, :dependent => :destroy)
has_many :companies, :through => :companies_users
Appreciate any advice as I'm just starting to learn this!
What you have above is correct, in terms of the ActiveRecord relationships. If you'd like to read more on the subject I believe this is the best source: http://guides.rubyonrails.org/association_basics.html
One problem I see there is that CompaniesUsers should be in singular form: CompanyUser, and then in all cases where you use :companies_users use: :company_users
I am assuming here that the current company of the User is the last one assigned.
Now in order to serialize in JSON format you should add the following in your User ActiveRecord:
def serializable_hash(options = nil)
options ||= {}
h = super(options)
if(defined?self.company_users.last and defined?(self.company_users.last).role)
h[:role] = (self.company_users.last).role
else
h[:role] = 'default_value'
end
end

Defining relationship in a model and updating the database

I am new to ruby on rails. As a part of my learning I created a sample application. In the sample app i have 2 models : tweet, user
I defined the relationship in my model, which defines the following : A Tweet has a User , A User has many Tweets. Here is how my code looks like :
class User < ActiveRecord::Base
attr_accessible :user_email, :user_name, :user_password
has_many :tweet
end
class Tweet < ActiveRecord::Base
attr_accessible :post_title
belongs_to :user
end
Now I want to get this updated in my database. I wanted to check if there is anyway this can be updated automatically e.g. running rake db:migrate or something else. Or if I have do it manually ?
Thanks for all your help.
First of all add a column user_id in your tweet table, which will store the user's id, This user_id will be the key to identify the tweets of an user. And then change the line
has_many :tweet
to
has_many :tweets
and it will work.
Thanks

Rails 3 - associations

I print in my view a number that tell me, how many people read my article. It looks something like a:
<%=article.hits.count%>
As is possible to see, I created a simple association.
Now I am trying to get the information, if the user who is log in on my page, so if he is already had read this article. In my table that contains hits is column user_id.
But I can't still find the way, how to get...
I tried something like:
<% if session[:login_user_id].hits.user_id == session[:login_user_id]%>
Have you read it already.
<% end %>
But the example above doesn't work me... Could anyone help me please, how to do?
EDIT: The models:
class Article < ActiveRecord::Base
has_many :hits
end
class Hits < ActiveRecord::Base
belongs_to :article, :class_name => "DataHit", :foreign_key => "article_id"
has_many :users
end
class User < ActiveRecord::Base
belongs_to :hit
end
Thanks in advance
Let's first talk about the model you like to receive. For me, it sounds like:
Every article can be visited / read by many users.
Every user can read / visit many articles.
This is a classical n:m-association which is normally implemented by a has-many-through association.
If this is the intention, it should be implemented like:
class Article < ActiveRecord::Base
has_many :hits
has_many :users, :through => :hits
end
class Hits < ActiveRecord::Base
belongs_to :article, :class_name => "DataHit", :foreign_key => "article_id"
belongs_to :user
end
class User < ActiveRecord::Base
has_many :hits
has_many :articles, :through => :hits
end
Of course, you have to add migrations that ensure that the final DB model is like that:
Hit has article_id and user_id to ensure that users may find the articles they have read
If you have that model implemented, it should be more easy. Then you have operations available like: #article.users.contains(User.find(user_id)). Have a look at the tutorial at Ruby on Rails Guides which explain what the has-many-through relation is and which advantages they have.
It would be helpful if you try the things first in the console of Rails. To do that, start with:
Start the rails console in the root directory of your application: rails c
Enter there e.g.: art = Article.find(1) to get the article with the id.
Try which methods are available: art.methods.sort to see all methods that could be used. If there is no method users, you have did something wrong with the assocication.
Try the call: us = art.users and look at the result. It should be a rails specific object, an object that behaves like a collection and understands how to add and remove users to that collection (with the whole life cycle of rails). The error your currently have could mean different things:
Your database model does not match your associations defined in Rails (I suspect that).
Some minor tweak (misspelling somewhere) which hinders Rails.
I hope this gives you some clues what to do next, I don't think that we can fix the problem here once and for all times.

Filter based on model attribute has_many relationship through, rails 3?

I have a simple question, but can't seem to find any solution, though I have found things that are similar, but just not exactly what I am looking for.
I have an application where a User has many Assets through the class UserAsset. I want to be able to do current_user.user_assets , but I only want to return records that have an Asset with a specified field value of "active".
This post is similar but I need to use the main model not the join model as a filter.
class UserAsset < ActiveRecord::Base
belongs_to :asset
belongs_to :user
end
class Asset < ActiveRecord::Base
has_many :user_assets
has_many :users, :through => :user_assets
end
class User < ActiveRecord::Base
has_many :user_assets
has_many :assets, :through => :user_assets
end
I tried setting the default scope on Asset, and also some conditions on the has many (user_assets) relationship, but rails is failing to consider the join on the Assets table. ie Unknown column 'asset.live' in 'where clause'. Trying to achieve the following:
#active_user_assets = current_user.user_assets #only where assets.active = true
So how do I use conditions or scopes to achieve this? I need the user_asset object because it contains info about the relationship that is relevant.
Thanks in advance!
You want current_user.assets, then your scopes should work.
Oh, but you want the user_assets. Hmm. I think you need the :include clause to find() but where to put it, I can't be arsed to think of right now.
Perhaps
current_user.user_assets.find(:all, :include => :assets).where('asset.live=?', true)
(I'm not on Rails 3 yet, so that's going to be mangled)
Are you using :through when you really want a HABTM?