has_one association selecting specific field - ruby-on-rails-3

I have user model which has_one association with user_profile
And I want to select name field from user_profile instead of user_profile.*
I have tried,
user = User.first
user.user_profile.select(:name)
But this is not working.any way?

UPDATED:
It seems, that rails handles the other direction and one-to-one connections differently. You have two options:
1) Define the selected attributes in the association, it will always select those
has_one :user_profile, :select => [:name, :id]
2) Define a specific find in your model, where you can add select, like this:
def my_profile
UserProfile.find(self.user_profile_id)
end
....
my_profile.select(:name)
ORIGINAL:
In case of has_many direction, it works:
I've tried your method in my code, like:
= User.find("admin").article.select(:title).to_sql
It returns the correct sql:
SELECT title
FROM "articles"
WHERE "articles"."user_id" = 1364

Related

query include methods rails

I have a Student model and that model has_many subscriptions. I need to return all students who have subscriptions. this object must contain only the student's name, email, phone and status, in addition to the subscription id and recurrence_code. I thought something more or less like this but I can't succeed in the query:
students = Student.all.includes(:subscriptions)
students.each do |student|
student.select(:id, :name, :email, :phone, :status, subscription: {
methods: [:id, :recurrency_code]}
end
This is a classic inner join scenario
class Student < ApplicationRecord
has_many :subscriptions
end
class Subscription < ApplicationRecord
belongs_to :student
end
I find it helpful to break these problems into steps:
"Only Students where a Subscription record is present" is a standard inner join:
Student.joins(:subscriptions).uniq
"object must contain only the student's name, email, phone and status"
Student.joins(:subscriptions).select(:name, :email, :phone, :status).uniq
"in addition to the subscription id and recurrence_code"
students = Student.joins(:subscriptions)
.select(
'students.name, students.email,'\
'students.phone, students.status, '\
'subscriptions.id as subscription_id,'\
'subscriptions.recurrence_code as subscription_recurrence_code'
)
A few notes:
1. Using select with joins
#vee's SO Answer here points out:
If the column in select is not one of the attributes of the model on which the select is called on then those columns are not displayed. All of these attributes are still contained in the objects within AR::Relation and are accessible as any other public instance attributes.
This means if you load an individual record (e.g. students.first), you will only see the Student attributes by default. But you can still access the Subscription attributes by the as name you set in the query. E.g.:
students.first.subscription_recurrence_code
2. Use .uniq to eliminate duplicates.
Because of the has_many relationship, the query Student.joins(:subscriptions) will return a record for each subscription, which means each student will appear in the result the same number of times as they have subscriptions. Calling .uniq (short for unique) will remove duplicates from the result.
I'm agree with the Chiperific response, but I disagree to use the uniq method because it doesn't call the 'DISTINCT' in the SQL query.
Rails: uniq vs. distinct
For me it's better to use distinct. So the query could be as this:
Student.joins(:subscriptions).distinct.select(
:name, :email, :phone, :status,
'subscriptions.id AS subscription_id',
'subscriptions.recurrence_code'
)

Rails - Search for object based on association's value

I've got two models Company and InsuredObject. Company has_many InsuredObjects and the reverse belongs_to. Currently, I have a functioning search for InsuredObject(s) that returns all objects that contain the search input as shown below:
# /models/insured_objects.rb
def self.search(search)
query = "%#{search}%"
if search
where("object LIKE ? OR insurance_type LIKE ? OR class_code LIKE ? OR information LIKE ?",
query, query, query, query)
end
end
and:
# /controllers/insured_objects_controller.rb
def index
#insured_objects = InsuredObject.search(params[:search])
end
Each Company has a is_active attribute. I'm trying to think of a way to search for the same thing but only return InsuredObject(s) that their Company's is_active attrutbute is true. Any thoughts?
Get all entries of InsuredObject from active companies:
InsuredObject.joins(:company).where(companies: {is_active: true})
In joins(:company), :company is a name of association (in InsuredObject you should have belongs_to :company)
In where(companies: ..., :companies is a table name for model Company

Query using condition within an array

I have 2 models, user and centre, which have a many to many relationship.
class User < ActiveRecord::Base
attr_accessible :name
has_and_belongs_to_many :centres
end
class Centre < ActiveRecord::Base
attr_accessible :name, :centre_id, :city_id, :state_id
has_and_belongs_to_many :users
end
Now I have an user with multiple centres, and I want to retrieve all the centres that have the same "state_id" as that user.
This is what I am doing now
state_id_array = []
user.centres.each do |centre|
state_id_array << centre.state_id
end
return Centre.where("state_id IN (?)", state_id_array).uniq
It works, but it's very ugly. Is there a better way for achieving this? Ideally a one line query.
UPDATE
Now I have
Centre.where('centres.state_id IN (?)', Centre.select('state_id').joins(:user).where('users.id=(?)', user))
The subquery work by itself, but when I tried to execute the entire query, I get NULL for the inner query.
Centre.select('state_id').joins(:user).where('users.id=(?)', user)
will generate
SELECT state_id FROM "centres" INNER JOIN "centres_users" ON "centres_users"."centre_id" = "centres"."id" INNER JOIN "users" ON "users"."id" = "centres_users"."user_id" WHERE (users.id = (5))
Which return 'SA', 'VIC', 'VIC'
but the whole query will generate
SELECT DISTINCT "centres".* FROM "centres" WHERE (centres.state_id IN (NULL,NULL,NULL))
Does user also has state_id column if yes then try this,
User.joins("LEFT OUTER JOIN users ON users.state_id = centers.state_id")
else
try User.joins(:center)
Solved.
.select(:state_id)
will retrieve a model with only the state_id column populated. To retrieve a field, use
.pluck(:state_id)
Below is the final query I had
Centre.where('centres.state_id IN (?)', Centre.joins(:user).where('users.id=(?)', user).pluck('state_id').uniq)

Rails - Pass current_user into SQL query

I'm looking for a way to pass the Rails/Devise variable "current_user" into a SQL query.
I have an app with two models, Users and Tips. Users can friend other users and send tips to each other. I'm trying to display a user's friend list ordered by the number of tips that the user has sent to each friend, so that the friend to whom the user has sent the most tips shows up at the top of the friend list, and so on.
I've read that RoR isn't equipped to handle this kind of query easily, so I've been able to put together the following SQL query, which works fine:
def friend_list
#friends = User.find_by_sql("SELECT users.*,
COUNT(tips.id) AS c FROM users, tips
WHERE tips.recipient_id = users.id
AND tips.user_id = 3
GROUP BY users.id ORDER BY c DESC")
end
The only problem is, I have manually entered a user.id there ("3") when ideally that "3" would be replaced by "current_user" so that each time a user loads this list they get their friend list ranked by who they themselves have sent tips to, rather than everyone just seeing user 3's ranking.
In an ideal world this would look something like:
AND tips.user_id = current_user
but that doesn't work. How can I pass a variable into this query so that it is different for each person viewing?
UPDATE:
User model (excerpt):
has_many :tips
has_many :received_tips, :class_name => "Tip", :foreign_key => "recipient_id"
Tip model (excerpt):
belongs_to :user
belongs_to :recipient, :class_name => "User"
You cannot access current_user in models if using devise...though there are few good ways to do it
##method one---in your controller
#users=User.find(current_user.id)
##method two---in your controller
#users=User.get_user_details(current_user.id)
##in model
def self.get_user_details(current_user_id)
User.find(current_user_id)
end
###you can also use in this way in controller
#all_videos = Video.where("videos.user_id !=?",current_user.id)
.........so this can be your solution............
##in controller or pass current_user and user it in your model
def friend_list
#friends = User.joins(:tips).select("users.* and count(tips.id) as c").where ("tips.users_id= ?",current_user.id).group("users.id").order("c DESC")
##or
#friends = User.all(:joins => :tips, :select => "users.*, count(tips.id) as tips_count", :group => "users.id",:order=>"tips_count DESC")
end
You can try this
def friend_list
#friends = User.find_by_sql("SELECT users.*,
COUNT(tips.id) AS c FROM users, tips
WHERE tips.recipient_id = users.id
AND tips.user_id = ?
GROUP BY users.id ORDER BY c DESC", current_user.id)
end
Anyway it is not that hard to do this query using active_record.
I know this is old, but I want to add that you could also use string injection.
def friend_list
#Declare the query string you want to pass
#query = "SELECT users.*,
COUNT(tips.id) AS c FROM users, tips
WHERE tips.recipient_id = users.id
AND tips.user_id = #{current_user.id}
GROUP BY users.id ORDER BY DESC"
#friends = User.find_by_sql(#query)
end

Selecting models through Rails relations?

I have these (simplified) models:
Address
public (Boolean)
has_one :group_member
Group
has_many :Group_Members
belongs_to :User
Group_Member
belongs_to :group
belongs_to :address
User
has_many :groups
I want to select all addresses where public is true and all the addresses which User can access through Groups.
I assume it's something along the lines of:
Address.where(public: TRUE).joins(:group_member)
Am I somewhere close?
I'm using Rails 4 and PostgreSQL as my database if it helps anyone.
I think this will work:
Address.joins(group_member: :group).where(is_public: true, groups: {user_id: 12345})
Let's break this down.
First we are calling on the Address model, b/c that is what we want to return.
.joins(:group_member) will join addresses to group_members via the Address has_one :group_member relation.
However, we actually want to go further, and join the group connected with the group_member, so we use a nested join, which is why it looks like this joins(group_member: :group) to indicate that we join address -> group_member, then group_member -> group.
Next the where clause. There are 2 conditions, we want:
Public addresses only, which is indicated as a column on the address, so we add this:
.where(is_public: true).
We want only where a particular user is connected with a group (and so its group_members and addresses). For this we need to add a nested where clause, like so groups: {user_id: 12345}.
The result of combining these is:
where(is_public: true, groups: {user_id: 12345})
So all together, the line of code above should get what you want.
Try the following -- it is not a single query but I think it is better than a single query with big join table:
public_addresses = Address.where(is_public: true)
# => get all public addresses
user_addresses = current_user.groups.includes(:group_members => :address).
# includes is to eager load records to avoid N+1 queries
flat_map{|g| g.group_members.map(&:address)}
# inner block returns array of addresses for each group
# flat_map converts the array of arrays to single level
# => get all addresses associated with the user
all_addresses = (public_addresses + user_addresses).uniq
# => remove duplicates
To speed up the query add indices for slower queries. For e.g
add_index :groups, :user_id
# this speeds up finding groups for a given user
add_index :group_members, :group_id
# this speeds up finding group_members for a given group
add_index :addresses, :group_member_id
# this speeds up finding addresses for a given group_member
Other Options is to get user_addresses using join tables
user_addresses = Address.joins(group_member: group).where(groups: {user_id: current_user.id} )