Rails has_one with join table - ruby-on-rails-3

In my app there are Athletes... athletes can have many sports.
Athlete:
has_many :sports, :through => :user_sports
has_one :primary_sport, conditions: ["user_sports.primary = ?", true], class_name: "Sport"
has_many :user_sports
UserSport:
class UserSport < ActiveRecord::Base
attr_accessible :athlete_id, :sport_id, :primary
belongs_to :athlete
belongs_to :sport
end
I am trying to be able to pull back the primary_sport as a Sport object instead of the user_sport object.

Since you pull your sports objects :through user_sports, you should pull your primary_sport object :through user_sports as well.
has_one :primary_sport, :through => :user_sports, conditions: ["user_sports.primary = ?", true], class_name: "Sport"

Related

Order users based on when the relationship was created_at

I used the Railstutorials to create followers and followed_users http://ruby.railstutorial.org/chapters/following-users#top
On the page where I want to show a specific persons' followers/followed_users, I'd like to show them based on when the relationship was created.
#users = #user.followers.order("created_at DESC")
Something like this ^^ just shows when the user was created, not when the relationship was created. How can I run this query efficiently to get the proper ordering?
def following
#users = #user.followed_users
end
def followers
#users = #user.followers
end
-User Model-
has_many :relationships, foreign_key: "follower_id", :dependent => :destroy
has_many :followed_users, through: :relationships, source: :followed
has_many :reverse_relationships, foreign_key: "followed_id",
class_name: "Relationship",
dependent: :destroy
has_many :followers, through: :reverse_relationships, source: :follower
- Relationship Model -
belongs_to :follower, class_name: "User", touch: true
belongs_to :followed, class_name: "User", touch: true
validates :follower_id, presence: true
validates :followed_id, presence: true
Since your user has there two relationships, you can easily access that table with the direction you want.
has_many :relationships, foreign_key: "follower_id", dependent: :destroy
has_many :reverse_relationships, foreign_key: "followed_id"
First Answer (when they're a follower)
You have to use the relationships table because that record gets created when you get a new follower, thus you do this:
#user.relationships.order("created_at DESC").collect { |r| User.find(r.followed) }
Second Answer (when they're followed)
#user.reverse_relationships.order("created_at DESC").collect { |r| User.find(r.follower) }

Rails joins through association

In Ruby on Rails, I want to find employers in the city.
Lets say the models are set up this way:
City
has_many :suburbs
has_many :households, :through => suburbs
has_many :people, :through => suburbs
Suburb
has_many :households
has_many people, :through => households
belongs_to :city
Household
has_many :people
belongs_to :suburb
People
belongs_to :household
belongs_to :employer
Employer
has_many :people
I feel like I want some sort of Employer joins some_city.people but I don't know how to do this. If people belonged directly to cities, I could join Employer to people where city_id is something, but I want to find the same data without that direct join and I am a little lost.
Thank you.
Use nested joins
Employer.joins({:people => {:household => {:suburb => :city}}})
should give you the join table you're looking. If you were traversing the other direction you would use plural names
City.joins( :suburbs => {:households => {:people => :employers }})
You can do the join like jvans has illustrated. Or you can setup your relationships like the following:
class Employer < ActiveRecord::Base
has_many :people
has_many :households, through: :people
has_many :suburbs, through: :households
has_many :cities, through: :suburbs
end
class Person < ActiveRecord::Base
belongs_to :household
belongs_to :employer
end
class Household < ActiveRecord::Base
belongs_to :suburb
has_many :people
end
class Suburb < ActiveRecord::Base
belongs_to :city
has_many :households
has_many :people, through: :households
end
class City < ActiveRecord::Base
has_many :suburbs
has_many :households, through: :suburbs
has_many :people, through: :households
has_many :employers, through: :people
end
Then you can join City from Employer, and vice-versa, directly.
For example:
Employer.joins(:cities).where("cities.name = ?", "Houston").first
SELECT "employers".* FROM "employers"
INNER JOIN "people" ON "people"."employer_id" = "employers"."id"
INNER JOIN "households" ON "households"."id" = "people"."household_id"
INNER JOIN "suburbs" ON "suburbs"."id" = "households"."suburb_id"
INNER JOIN "cities" ON "cities"."id" = "suburbs"."city_id" WHERE (cities.name = 'Houston')
LIMIT 1

Rails 3 - WARNING: Can't mass-assign protected attributes: user_ids

I have a has_many through relationship between Course and User.
class Course < ActiveRecord::Base
belongs_to :user
has_many :enrollments, :dependent => :delete_all
has_many :users, :through => :enrollments
attr_accessible :description, :duration, :name, :prerequisites, :short_name, :start_date, :user_id
accepts_nested_attributes_for :users, :allow_destroy => true
attr_accessible :users_attributes
and User:
class User < ActiveRecord::Base
has_many :subjects, :class_name => "Course" # to get this call user.subjects
has_many :enrollments, :dependent => :delete_all
has_many :courses, :through => :enrollments
and Enrollment:
class Enrollment < ActiveRecord::Base
belongs_to :course
belongs_to :user
attr_accessible :course_id, :user_id
end
Now I'm trying to set user_ids from inside Course, using a nested form. It keeps giving me that Mass Assignment warning, and nothing is saved. I read I was supposed to add attr_accessible user_id but it still doesn't work.
Even if I do something like this from the rails console:
#c.update_attributes({:user_ids => [7,8]})
with #c being the course
Any help would be greatly appreciated,
Thank you.
It's user_ids, not user_id.
You need to add user_ids to your attr_accessible.

Rails ActiveRecord Query (Cross model)

I have an app that lets users input dates & interests that relate to those dates .I need to send them deals (a few days before the date - Via Email) that are based off of their interests and location. I have all the models setup and recording the data properly, just wondering how to query the models for the dates and then send the appropriate deal based off of the city and interests.
Notes:
*Each city and interest category has only 1 deal
*I have several different models for types of dates (Holidays, Occasions, Friends Birthdays ect).. all are pretty much identical in structure.
*All interests for each type of date are stored in person_interests.
Models:
Class User
belongs_to :province
belongs_to :city
has_many :friends_bdays
has_many :occasions
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
has_many :user_holidays
has_many :holidays, :through => :user_holidays
has_many :anniversaries
end
class Deal < ActiveRecord::Base
belongs_to :interest
belongs_to :city
belongs_to :store
end
class Store < ActiveRecord::Base
has_many :deals
belongs_to :city
belongs_to :province
end
class PersonInterest < ActiveRecord::Base
belongs_to :interest
belongs_to :person, :polymorphic => true
end
class Interest < ActiveRecord::Base
has_many :person_interests
has_many :deals
end
class Occasion < ActiveRecord::Base
belongs_to :user
belongs_to :admin_user
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
end
class Anniversary < ActiveRecord::Base
belongs_to :user
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
end
class Friend_bday < ActiveRecord::Base
belongs_to :user
has_many :person_interests, :as => :person
has_many :interests, :through => :person_interests
end
You can achieve this using a variation of the solution below:
Install the squeel gem
class User
def deals(reload=false)
#deals = nil if
#deals ||= Deal.where{
( (:city => city_id) | ( :interest_id => interest_ids) ) &
:deal_date => (Time.now..3.days.from_now)
}
end
end
Now, user.deals returns the deals that will be active in next 3 days matching the user's city OR interests.
Edit 1:
Based on your comment it looks like you don't need the squeel gem. You can achieve what you want using regular AR syntax.
class User
def deals(reload=false)
#deals = nil if reload
#deals ||= Deal.where(
:city => city_id,
:interest_id => interest_ids,
:deal_date => (Time.now..3.days.from_now)
)
end
end

Has_many, through association

Warning:Total Rails Newb (TRN). This should be a pretty basic question so I'm hoping someone can spare a couple mins to help shed some light.
Let's say I have the following models: User, Group, and Member
A user can have many groups (let's say friends, family, etc)
A group can have many members, namely other users.
How would I structure this?
Initially I tried this:
class User < ActiveRecord::Base
has_many :groups
has_many :groups, :through => :members
end
class Groups < ActiveRecord::Base
has_many :users, :through => :members
belongs_to :user
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
However this gave me an error in User so I changed
has_many :groups, :through => :members
to
has_many :memberships, :through => :members, :source => :groups
Still getting an error about missing association when I try to do
group = Group.new
group.user.new
It will be useful: http://railscasts.com/episodes/47-two-many-to-many
class User < ActiveRecord::Base
has_many :members
has_many :groups, :through => :members
has_many :groups_as_owner, :class_name => "Group"
end
class Groups < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
belongs_to :owner, :class_name => "User", :foreign_key => :user_id
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
basically has_many-through associations are n:m associations (join-tables) that (shall) have more attributes than just the id's of the joined record ids...
so you have a table Groups (with an id), a table Users (with an id) and a table Members (no id, but user_id and group_id)
basically, what you did is nearly correct, just think about how you access a group from a user or vice versa....
a user would first look up its member information and through that member information get access to the group information ... and vice versa for a group
so you first set up
has_many :members
and then call
has_many :groups, :through => :members
all you need is
class User < ActiveRecord::Base
has_many :members
has_many :groups, :through => :members
end
class Groups < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
and you have another bug in your code above
you might want to use
user = group.users.new
instead of
user = group.user.new
Try this structure:
class User < ActiveRecord::Base
has_many :members
has_many :groups, :through => :members
end
class Groups < ActiveRecord::Base
has_many :members
has_many :users, :through => :members
end
class Member < ActiveRecord::Base
belongs_to :group
belongs_to :user
end
Also take a look at has_and_belongs_to_many, if you don't need to do with class Member then you should use has_and_belongs_to_many. In this case don't forget to create joining table in the database