Rails: Multiple "has_many through" for the two same models? - ruby-on-rails

Can't wrap my head around this...
class User < ActiveRecord::Base
has_many :fantasies, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasy < ActiveRecord::Base
has_many :users, :through => :fantasizings
has_many :fantasizings, :dependent => :destroy
end
class Fantasizing < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... which works fine for my primary relationship, in that a User can have many Fantasies, and that a Fantasy can belong to many Users.
However, I need to add another relationship for liking (as in, a User "likes" a Fantasy rather than "has" it... think of Facebook and how you can "like" a wall-post, even though it doesn't "belong" to you... in fact, the Facebook example is almost exactly what I'm aiming for).
I gathered that I should make another association, but I'm kinda confused as to how I might use it, or if this is even the right approach. I started by adding the following:
class Fantasy < ActiveRecord::Base
...
has_many :users, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class User < ActiveRecord::Base
...
has_many :fantasies, :through => :approvals
has_many :approvals, :dependent => :destroy
end
class Approval < ActiveRecord::Base
belongs_to :user
belongs_to :fantasy
end
... but how do I create the association through Approval rather than through Fantasizing?
If someone could set me straight on this, I'd be much obliged!

Keep your first set of code, then in your User Model add:
has_many :approved_fantasies, :through => :fantasizings, :source => :fantasy, :conditions => "fantasizings.is_approved = 1"
In your Fantasizing table, add an is_approved boolean field.

Related

In Rails, how would you merge these two different has_manys into one?

Let's say we have a User.
A user has_many documents through account like so…
class User < ActiveRecord::Base
belongs_to :account
has_many :documents, :through => :account, :order => "created_at DESC"
end
class Account < ActiveRecord::Base
has_one :owner, :class_name => "User", :dependent => :destroy
has_many :documents, :dependent => :destroy
end
class Document < ActiveRecord::Base
belongs_to :account
end
Nice and simple, this is where it gets tricky…
A user can also collaborate on documents, this via the collaborators join table…
class Collaborator < ActiveRecord::Base
belongs_to :user
belongs_to :documnet
end
class Document < ActiveRecord::Base
has_many :collaborators, :dependent => :destroy
has_many :users, :through => :collaborators
accepts_nested_attributes_for :collaborators, :allow_destroy => true
end
The final user bit of this is what i'm not sure about. I want to add another has many documents, and when you call user.documents it blends both documents via their account and the ones they're collaborating on…
class User < ActiveRecord::Base
belongs_to :account
has_many :documents, :through => :account, :order => "created_at DESC"
#documents need to do both has manys…
has_many :collaborators, :dependent => :destroy
has_many :documents, :through => :collaborators
end
Thanks, it's a bit long but I can think of a neat solution. Any help would be much appreciated.
You can create a method that will request on the tables documents, accounts and collaborators to find the documents related to the user:
class User < ActiveRecord::Base
#...
def documents
Document.includes(:account, :collaborators).where('collaborators.user_id = ? OR documents.account_id = ?', self.id, self.account.id)
end
end
I've not tested this request, but I hope you get the idea. Please correct it if it's erroneous.
For the 2 has_many documents, :through..., you can remove them if you don't need them anymore; Otherwise, you have to give them different names (and different from the method above).

Rails model relationships with custom names

EDIT
Simple relationship but I'm having an issue getting it to work. There is a User. User's have many Bounties. User's have many BountyVotes through Bounty. Bounties have BountyVotes. For readability, I call BountyVotes -> Votes in the Bounty class. I get a Name Error: uninitialized constant User::bounty_vote when trying to access bounty_interests from the User model.
A user can create a Bounty. Other user's can vote on the Bounty.
//User class
class User < ActiveRecord::Base
has_many :bounties
has_many :bounty_interests, :through => :bounties, :source => :votes
end
//Bounty class
class Bounty < ActiveRecord::Base
belongs_to :user
has_many :votes, :class_name => :bounty_vote
end
//Bounty Vote class
class BountyVote < ActiveRecord::Base
belongs_to :bounty
end
Had to change two things. First, thanks to shakerlxxv I needed to change my through to be plural.
has_many :bounty_interests, :through => :bounties, :source => :votes
Than I also had to change the way I referenced my class name.
has_many :votes, :class_name => 'BountyVote'
Your :through clause needs to reference the plural form:
has_many :bounty_interests, :through => :bounties, :source => :votes

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

Trying to design a controller to display courses (Rails)

Below are the models that are relevant to my problem. I am attempting to design a way to display CourseEnrollments along with their steps to a given patient. Here is what I have come up with so far.
INDEX ACTION - /course_enrollments/ --> Display courses user is enrolled in as well as the most recent course overview. This can redirect to most recent course.
SHOW ACTION - /course_enrollments/:id --> Display courses user is enrolled in as well as the most recent course overview
The part I am struggling to figure out is how to display an individual step for a course. Should this be done in the course_steps controller (which is nested inside the courses resource)?
class Course < ActiveRecord::Base
belongs_to :course_category
belongs_to :client
belongs_to :user_created, :foreign_key => :user_created_by, :class_name => "User"
belongs_to :user_updated, :foreign_key => :user_last_updated_by, :class_name => "User"
has_many :course_steps, :dependent => :destroy
has_many :steps, :through => :course_steps
has_many :course_requests, :dependent => :destroy
has_many :course_enrollments, :dependent => :destroy
has_many :patients, :through =>:course_enrollments
end
class CourseStep < ActiveRecord::Base
belongs_to :step
belongs_to :course
validates_uniqueness_of :step_id, :scope => :course_id
end
class Step < ActiveRecord::Base
belongs_to :step_type
belongs_to :client
has_one :step_quiz, :dependent => :destroy
has_one :step_survey, :dependent => :destroy
has_one :step_text, :dependent => :destroy
has_one :step_download, :dependent => :destroy
has_one :step_video, :dependent => :destroy
has_one :step_presentation, :dependent => :destroy
has_many :course_steps, :dependent => :destroy
has_many :courses, :through => :course_steps
end
class CourseEnrollment < ActiveRecord::Base
belongs_to :patient
belongs_to :course
end
class Patient < ActiveRecord::Base
belongs_to :user, :dependent => :destroy
has_many :enrollments, :dependent => :destroy
has_many :clients, :through => :enrollments
has_many :course_requests, :dependent => :destroy
has_many :course_enrollments, :dependent => :destroy
has_many :courses, :through => :course_enrollments
end
The usual approach is to nest these and have a compound sort of path, but how you route these things is often dependent on the level of context involved. For instance, is the display of a course driven by a user to the degree that the path should have the user in it, or is it a case of simply personalizing the course page?
Typically you see things like this:
resources :courses do |course|
course.resources :enrollments
course.resources :steps
end
There's usually a correlation between your has_many relationships and declaring an equivalent resources but not always.
Have a look at the generated routes using rake routes to see what the parameters will be called when passed to your controller, as well as what the expected controller name will be. You can customize the name of the controller by passing in a :controller option to the route.
Typically the last record in the path is passed in as :id whereas the prior ones are named, such as :course_id. This is a somewhat annoying inconsistency, so do be careful to check you're loading using the correct parameters.
If you need to display an individual step for a course, you definitely need to use the course_steps controller, logic being, each CourseStep object is a combo of one-course and one-step.

Checking a related relationship once the instance is created

I have a schema where:
Students
has_and_belongs_to_many :courses
has_many :grades, :dependent => :destroy
has_many :assignments, :through => :grades
Courses
has_many :assignments, :dependent => :destroy
has_and_belongs_to_many :students
Assignments
belongs_to :course
has_many :grades, :dependent => :destroy
has_many :students, :through => :grades
Grades
belongs_to :student
belongs_to :assignment
I would like to add functionality whereby if a grade is added and the student does not belong to the course that the grade's assignment belongs to, then this relationship is made. Any suggestions as to the best way to do this? The grades_courses table does not have it's own model, will this need to be made?
A friend has suggested using after_create, but I don't know how to pass the parameters to this.
How about an observer on grades? Something like this
class GradeObserver < ActiveRecord::Observer
def after_create(grade)
unless grade.assignment.course.students.include?(grade.student)
grade.assignment.course.students << grade.student
end
end
end

Resources