I know that isn't any belongs_to :through association in Rails, but how could I work around it?
User model has many Batches
Batch model has many Forecasts
I want to create an association stating that a Forecast belongs to a User. However, I understand that since a Forecast already belongs to a User, it shouldn't be necessary to store used_id inside Forecast model.
How can I create this relationship between Forecast and User through the Batch model?
EDIT
Let me better explain my problem.
I have the following models and associations:
User < ActiveRecord::Base
has_many :batches
# has_many :forecasts, through: :batches
end
Batch < ActiveRecord::Base
belongs_to :user
has_many :forecasts
end
Forecast < ActiveRecord::Base
belongs_to :batch
# belongs_to :user, through: :batch
end
The commented lines is what I want to do, but I cannot, since there is no belongs_to :through association.
The relationship is strictly what is said on the code: a User may have many batches. A batch is a set of forecasts, so a Batch may have many forecasts. Since a Batch must belong to a User, and a Forecast must belong to a Batch, then a Forecast belongs to a User.
I want to get select all Forecasts that belongs to all Batches from a User.
current_user.forecasts
The reason I want to do that, is to limit the scope of a user only to its own batches and forecasts. So on controller actions, instead of
Batch.find(params[:id])
or
Forecast.find(params[:id])
I can make
current_user.batches.find(params[:id])
or
current_user.forecasts.find(params[:id])
It is already possible to do that with Batch. Forecasts, on the other hand, doesn't belongs_to a user yet. How can I do that?
There is has_many :through in Rails. You can check out the Rails guide to see how to use it.
In your User model:
has_many :batches
has_many :forecasts, :through => :batches
In your Batches model:
belongs_to :user
belongs_to :forecasts
In your Forecasts model:
has_many :batches
has_many :users, :through => :batches
That last line is just if you want the relationship to go both ways, like a full many-to-many relationship.
Related
I have searched and searched and found only partial solutions to my current question.
The thing is, I'd like to know if it is possible to use has_many :through along with a polymorphic association in Ruby on Rails.
I have a system where students can create travel plans (that can belong to many students) and refund claims (that can belong to only one student) for their projects. In this system, both admin users and students are able to comment on the plans and claims.
My associations are:
class Student < ActiveRecord::Base
has_and_belongs_to_many :travel_plans
has_many :refund_claims
has_many :comments, through: :travel_plans
has_many :comments, through: :refund_claims
end
class AdminUser < ActiveRecord::Base
has_many :comments
end
class TravelPlan < ActiveRecord::Base
has_and_belongs_to_many :students
has_many :comments, as: :commentable
end
class RefundClaim < ActiveRecord::Base
belongs_to :student
has_many :comments, as: :commentable
end
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
end
My questions are:
Is it correct to associate comments twice in the Student model?
I don't want the AdminUsers to have travel plans and refund claims, how can I identify their comments as being made on a travel plan or on a refund claim?
Would there be a better approach?
Thanks a lot in advance for everyone!
Cheers,
You probably want to add an polymorphic author attribute to the Comment model. Than you just need has_many :comments, as: :author to the Student and AdminUser model.
If this is a new application and you are starting on the green field you might want to rethink your models a bit and add a Role and a User model. Student would than be a role of user as would AdminUser be.
Is it correct to associate comments twice in the Student model?
No, not really. If you have duplicate association name, you can only use one of them. If you want to use both, you have to name them differently.
So I'm Rails n00b and I want to create a "favorites" relationship such that a User can have many favorite Item. I'm not entirely sure how to do this, this is how I'm going to try but I'm not sure if this is a good practice at all:
class User < ActiveRecord::Base
has_many :favorites
//other code
end
class Favorite < ActiveRecord::Base
belong_to :user
has_one :item
end
class Item < ActiveRecord::Base
belongs_to :item
end
Is this a good way to do it? Should I be using has_and_belongs_to_many ?
I'm specially concerned in the following scenario: Say a user has 100 favorite items.
When I do a User.find(id) will I also be retrieving the 100 favorites and the 100 Items?
In case it's important: ruby version 1.9.3, rails version 3.2.11
Can you try has_many => :through?
class User < ActiveRecord::Base
has_many :favorites
has_many :items, :through => :favorites
//other code
end
In your case has_many :through is definitely the way to go. I would recommend reading: http://guides.rubyonrails.org/association_basics.html
Of particular interest with regard to your question:
2.8 Choosing Between has_many :through and has_and_belongs_to_many
Rails offers two different ways to declare a many-to-many relationship between models. The simpler way is to use has_and_belongs_to_many, which allows you to make the association directly:
class Assembly < ActiveRecord::Base
has_and_belongs_to_many :parts
end
class Part < ActiveRecord::Base
has_and_belongs_to_many :assemblies
end
The second way to declare a many-to-many relationship is to use has_many :through. This makes the association indirectly, through a join model:
class Assembly < ActiveRecord::Base
has_many :manifests
has_many :parts, :through => :manifests
end
class Manifest < ActiveRecord::Base
belongs_to :assembly
belongs_to :part
end
class Part < ActiveRecord::Base
has_many :manifests
has_many :assemblies, :through => :manifests
end
The simplest rule of thumb is that you should set up a has_many :through relationship if you need to work with the relationship model as an independent entity. If you don’t need to do anything with the relationship model, it may be simpler to set up a has_and_belongs_to_many relationship (though you’ll need to remember to create the joining table in the database).
You should use has_many :through if you need validations, callbacks, or extra attributes on the join model.
It is better than using has_and_belongs_to_many.
When I do a User.find(id) will I also be retrieving the 100 favorites
and the 100 Items?
No. You'll just get the user object.
Update:
Calling User.include(:favourites, :items).find(id) will get you joined tables in case you want to make many calls to items table from user object.
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
I'm in the process of trying to develop my first rails application and before I jump off into actually implementing my ideas, I was hoping I could get some help with my association planning.
My application is going to be an educational tool and I have some basic functionality that I need to implement. I want to have students that can register for and create courses. Within these courses, there will be one user who is the teacher and the rest are students. Within the course will be assignments that the teacher creates and that users are required to make a submission for. These are the basics. Eventually I want to add more functionality but as of now I just need the foundations set.
Here are my ideas so far on what the associations should look like:
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
end
class Enrollment < ActiveRecord::Base
belongs_to :user # foreign key - user_id
belongs_to :course # foreign key - course_id
end
However, I'm running into my wall of inexperience at the moment with how to appropriately handle the rest of the associations at this point. I could probably hack out something, but I'd prefer to do it as best as I can the first time.
How do I handle the associations related to assignments and submissions? Presumably a student's submission should belong_to them, but it is also specifically related to an assignment within the class. Assignments originate within a course, but are also closely tied to the user.
Finally, what's the best way to handle the relationships between a user and the class they create? A user creates a course. Does that course belong_to them? Should I just add a column to the course's table for storing the creator's id? Etc.
Thanks for the help.
Suggestion:
You might want to separate out your Teacher and Student models, since you're very likely to have different actions associated with each (and while they share some attributes, they really are different entities in your model, for example, you likely want just one teacher teaching in a course.)
You could derive both the Teacher model and the Student model from a User model that has the shared attributes and authentication.
Now to your questions:
If you'd like to keep the student that created the course associated, creator_id is the way I'd go. (If a teacher can create a course too, deriving Student and Teacher from a shared User model would help)
Assignments and Submissions:
You've pretty much defined it in words. Here is the code.
[If different students within a course could get different assignments, only then do you want to build a direct association between Student and Assignment, otherwise, use a through association]
class User < ActiveRecord::Base
has_many :enrollments
has_many :courses, :through => :enrollments
has_many :assignments, :through => :courses
has_many :submissions
end
class Course < ActiveRecord::Base
has_many :enrollments
has_many :users, :through => :enrollments
has_many :assignments
end
class Enrollment < ActiveRecord::Base
belongs_to :user # foreign key - user_id
belongs_to :course # foreign key - course_id
end
class Assignment < ActiveRecord::Base
belongs_to :course
has_many :submissions
end
class Submission < ActiveRecord::Base
belongs_to :assignment
belongs_to :user
end
1)A user can have many causes and a cause can belong to many users.
2)A user can have many campaigns and campaigns can belong to many users. Campaigns belong to one cause.
I want to be able to assign causes or campaigns to a given user, individually. So a user can be assigned a specific campaign. OR a user could be assigned a cause and all of the campaigns of that cause should then be associated with a user.
Is that possible? And could I set it up so that the relationships could be simplified like so:
User.causes = all causes that belong to a user
User.campaigns = all campaigns that belong to user whether through a cause association or campaign association
This should work.
class User < ActiveRecord::Base
has_many :causes, :through => :cause_users
has_many :campaigns, :through => :campaign_users
# other model stuff
class Cause < ActiveRecord::Base
has_many :users, :through => :cause_users
has-many :campaigns
# other model stuff
class Campaign < ActiveRecord::Base
belongs_to :cause
has_many :users, :through => :campaign_users
# other model stuff
class CampaignUser < ActiveRecord::Base
belongs_to :campaign
belongs_to :user
# other model stuff
class CauseUser < ActiveRecord::Base
belongs_to :cause
belongs_to :user
# other model stuff
has_many :through requires that you create a new model for each of these joins: campaign_users and cause_users, as is shown but it provides more functionality later on than has_and_belongs_to_many.
I would also suggest using better names than :campaign_users and :cause_users so the relationship is more meaningful.
I believe you should use the following:
class User < ActiveRecord::Base
has_and_belongs_to_many :causes
has_and_belongs_to_many :campaigns
end
class Cause < ActiveRecord::Base
has_and_belongs_to_many :users
has_many :campaigns
end
class Campaign < ActiveRecord::Base
has_and_belongs_to_many :users
belongs_to :cause
end
This way you can use
User.causes
User.campaigns
Cause.campaing
Cause.users
Campaign.users
Campaign.cause
You can read here about has_and_belongs_to_many relationship, here about has_one and here about belongs_to.
Let me know if this is what you want :]
Edit:
"I would still need User.campaigns to
be campaigns from a user's causes or
individual campaigns associated with a
user"
You can have a method on users model that returns all campaigns. Something like this:
def all_campaigns
self.campaigns + self.causes.collect{ |c| c.campaigns }
end
You can make :has_many :through associations between users and campaigns using a join model, and also between users and causes using the another join model. The you can make a :has_many :campaigns association in the causes model, putting a :belongs_to :cause in the campaign model.
But you won't be able to fetch all the users campaigns or causes by User.campaigns.orders or User.order.campaigns. You should make an iteration over the User.campaigns collection or User.causes, fetching Campaign.cause or Cause.capaigns. Or even making a custom SQL query, using joins and conditions to filter information in the joins.