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

I have the following:
has_many :sports, :through => :user_sports
has_one :primary_sport, class_name: "UserSport", conditions: ["user_sports.primary = ?", true]
has_many :user_sports
When I run this in console:
athlete = Athlete.all.last
athlete.primary_sport
The record that is returned is the record from the join table instead of the record joining the sports table. Any way to return the actual sport from the join?

You might probably do something like this:
class UserSport < ActiveRecord::Base
has_many :athletes
has_many :sports
end
athlete = Athlete.all.last
athlete.primary_sport.sport
Didn't try it by myself, just check and see :)

Related

SQL How to query a has_many_through relationship

I have three models that have a has_many_through relationship like this:
class Tag < ActiveRecord::Base
has_many :tag_workspaces
has_many :workspaces, through: :tag_workspaces, dependent: :destroy
end
class Workspace < ActiveRecord::Base
has_many :tag_workspaces
has_many :tags, through: :tag_workspaces, dependent: :destroy
end
class TagWorkspace < ActiveRecord::Base
belongs_to :tag
belongs_to :workspace
end
I need to write a query using sql language to get all the tags that belong to a specific workspace.
So far my query looks like this:
select '0' as "value", 'Seleziona un Tag' as "name"
union all
SELECT name AS "value",
name
FROM tags
But I need to select only the tags that belong to workspace 1, for example. How could I accomplish that? Thank you so much!
In SQL, this would be ...
select t.*
from tags t
join tag_workspaces tw on tw.tag_id = t.id
join workspaces w on w.id = tw.workspace_id
where w.id = 1

Ordering records by an association presence

Lets say I have an instructions table which is associated to a surveys table through survey_instructions join table.
What I need to achieve is to fetch all instruction records, but ordered by an association presence with a given survey.
So instructions associated with a given survey will go first, and then all other instructions which have no association with this survey.
class Instruction < ApplicationRecord
has_many :survey_instructions, dependent: :destroy
has_many :surveys, through: :survey_instructions
and
class Survey < ApplicationRecord
has_many :survey_instructions, dependent: :destroy
has_many :instructions, through: :survey_instructions
and
class SurveyInstruction < ApplicationRecord
belongs_to :survey
belongs_to :instruction
and
Could this be achieved by chaining active record queries somehow? Would appreciate any thoughts on this
Yes you can achieve this by ActiveRecord query. Try this:
survay_id = 10
#instructions = Instruction.includes(:survey_instructions).order("(CASE WHEN survey_instructions.survay_id = #{survay_id} THEN 1 ELSE 2 END) ASC NULLS LAST")
Happy coding :)

rails select distinct nested associations and fetch those associations

i have the models User, Company, Product, View
class Company < ActiveRecord::Base
has_many :users
end
class Product < ActiveRecord::Base
has_many :views_by_user, -> { where viewable_type: User },
as: :viewable, class_name: "View"
end
class User < ActiveRecord::Base
has_many :viewed, as: :viewer, class_name: "View"
belongs_to :company
end
class View < ActiveRecord::Base
belongs_to :viewable, polymorphic: true
belongs_to :viewer, polymorphic: true
end
What i did with the above is, when a user views product, i save the data in the views
Now i want the list of distinct companies that have looked at my product(via user) and total count for my serializer. what i have done is,
distinct_users = #product.views_by_user
.includes(viewer: [:company])
.joins("left outer join users on views.viewer_id = users.id")
.select("distinct users.company_id, views.*")
but with this, i would have to do something like
distinct_users.will_paginate(...).map(&:viewer).map(&:company)
is there a better way to do it? also if i use distinct_users.count it throws me an error
PG::UndefinedFunction: ERROR: function count(integer, views) does not exist
LINE 1: SELECT COUNT(distinct users.company_id,...
Start from Company if this is the type of record you actually want. You can use merge to combine the conditions on a relation with those from another. Try this:
Company.joins(:users => :viewed).merge(View.where(viewable: #product))
HTH

How to write this ActiveRecord Query using Join instead of subquery in Rails 4

Consider the following:
class User < ActiveRecord::Base
has_many :events
end
class Event < ActiveRecord::Base
belongs_to :user #this user is the event owner
has_many :members
end
class Members < ActiveRecord::Base
belongs_to :user
belongs_to :event
end
Now, I need to list all the members for which current_user is the owner. so I have come up with this:
#members = Member.where event_id: current_user.events
which produces the following query:
SELECT "members".* FROM "members" WHERE "members"."event_id" IN (SELECT "events"."id" FROM "events" WHERE "events"."user_id" = 1)
This works as expected but uses subqueries instead of JOIN. Does anyone know a better way to write this same query?
Add a has_many :through association to your User model:
class User < ActiveRecord::Base
has_many :events
has_many :members, :through => :events
end
Now you can query for all a user's members through the members association:
user.members
The SQL generated will look something like:
SELECT "members".* FROM "members" INNER JOIN "events" ON "members"."id" = "events"."member_id" WHERE "events"."user_id" = 1
Transformed to JOIN syntax (with table aliases to make it shorter and easier to read):
SELECT m.*
FROM events e
JOIN members m ON m.event_id = e.id
WHERE e.user_id = $1
I guess this will work.
Member.joins(:event).where("events.user_id = ?" , current_user.id)
You could do something like :
Member.joins(:event).where(events: {user_id: current_user.id})

How to filter by association count?

Let's say I have models that look like this:
class Foo < ActiveRecord::Base
has_many :bars, :through => :cakes
has_many :cakes
end
class Bar < ActiveRecord::Base
has_many :foos, :through => :cakes
has_many :cakes
end
class Cake < ActiveRecord::Base
belongs_to :foo
belongs_to :bar
end
How would I get all foos which had 10 or more bars (and therefore 10 or more cakes)?
Foo.all(:joins => :cakes,
:group => "cakes.foo_id",
:having => "count(cakes.bar_id) >= 10")
okay i tried the answer above, but had a problem.
for our purposes Father has_many :sons, ok?
i wanted to find Fathers that had zero sons.
the above did not work, because it produced an inner join... thereby filtering out all fathers without sons.
the following did work for me:
Father.includes(:sons).group('fathers.id').having( 'count(sons.id)=0' )
and it also happens to work for any other filter you'd require
Father.includes(:sons).group('fathers.id').having( 'count(sons.id)=3' )