Ruby on rails fetch name and show from the has many through - ruby-on-rails

we have user table and also wee have a country table the association is given as
user.rb
has_many :user_countries
has_many :countries, :through => :user_countries
user_country.rb
belongs_to :user
belongs_to :country
country.rb
has_many :users, :through => :user_countries
now what i want is to fetch the country name and show it into the sql record such that the new user_country column should come which will show the country of the user while fetching users
data = User.select("users.*, countries.name as country_name").
joins("INNER JOIN `user_countries` ON `user_countries`.`user_id ` = `users`.`id`").
joins("INNER JOIN `countries` ON `countries`.`id ` = `user_countries`.`country_id`")
till now i am trying to do this but not succeeded it throws the mysql error . i need an extra column giving country_name as extra column. Please help me with this

since you already using activerecord associations, you can use active record query as the following:
User.joins(:countries).select("users.*, countries.name as country_name")
You can learn more about select here

Looks like you're trying to create a many to many relationship. This can be done very simply with a has_and_belongs_to_many association. Take a look at http://guides.rubyonrails.org/association_basics.html#the-has-and-belongs-to-many-association.

Related

nested queries for has_many :through in rails

Association of model is
class Campaign
has_many :views_logs
has_many :users, through: :views_logs
end
I want to get only those users of campaign where views_logs is created b/w specific dates.
Note: created_at query for views_logs not for Users creation
Campaign.joins(:users).where("views_logs.created_at = ?", my_date)
Joining Users like this will perform a SQL JOIN on your views_logs then a SQL JOIN on your users, which allows you to use a view_logs column in your WHERE condition.

Rails ActiveRecord how to order by a custom named association

Ok so have created 2 models User and Following. Where User has a username attribute and Following has 2 attributes which are User associations: user_id, following_user_id. I have set up these associations in the respective models and all works good.
class User < ActiveRecord::Base
has_many :followings, dependent: :destroy
has_many :followers, :class_name => 'Following', :foreign_key => 'following_user_id', dependent: :destroy
end
class Following < ActiveRecord::Base
belongs_to :user
belongs_to :following_user, :class_name => 'User', :foreign_key => 'following_user_id'
end
Now I need to order the results when doing an ActiveRecord query by the username. I can achieve this easily for the straight-up User association (user_id) with the following code which will return to me a list of Followings ordered by the username of the association belonging to user_id:
Following.where(:user_id => 47).includes(:user).order("users.username ASC")
The problem is I cannot achieve the same result for ordering by the other association (following_user_id). I have added the association to the .includes call but i get an error because active record is looking for the association on a table titled following_users
Following.where(:user_id => 47).includes(:user => :followers).order("following_users.username ASC")
I have tried changing the association name in the .order call to names I set up in the user model as followers, followings but none work, it still is looking for a table with those titles. I have also tried user.username, but this will order based off the other association such as in the first example.
How can I order ActiveRecord results by following_user.username?
That is because there is no following_users table in your SQL query.
You will need to manually join it like so:
Following.
joins("
INNER JOIN users AS following_users ON
following_users.id = followings.following_user_id
").
where(user_id: 47). # use "followings.user_id" if necessary
includes(user: :followers).
order("following_users.username ASC")
To fetch Following rows that don't have a following_user_id, simply use an OUTER JOIN.
Alternatively, you can do this in Ruby rather than SQL, if you can afford the speed and memory cost:
Following.
where(user_id: 47). # use "followings.user_id" if necessary
includes(:following_user, {user: :followers}).
sort_by{ |f| f.following_user.try(:username).to_s }
Just FYI: That try is in case of a missing following_user and the to_s is to ensure that strings are compared for sorting. Otherwise, nil when compared with a String will crash.

Accesing attributes in the joining table with has_many through

I have a many2many relationship with a has_many through association:
class User < ActiveRecord::Base
has_many :trips_users
has_many :trips, through: :trips_users
end
class Trip < ActiveRecord::Base
has_many :trips_users
has_many :users, through: :trips_users
end
class TripsUser < ActiveRecord::Base
belongs_to :user
belongs_to :trip
end
The joining table trips_user contains a column named 'pending' which id like get when I ask for a list of trips of a user.
So in my controller I need to get all trips a user has, but also adding the 'pending' column.
I was trying
current_user.trips.includes(:trips_users)
that will be done by this select statement:
SELECT trips.* FROM trips INNER JOIN trips_users ON trips.id
= trips_users.trip_id WHERE trips_users.user_id = 3
which is missing the information in the trips_users table that I want.
The desired sql would be:
SELECT trips.*, trips_users.* FROM trips INNER JOIN trips_usersON trips.id =
trips_users.trip_id WHERE trips_users.user_id = 3
This finally worked:
current_user.trips.select('trips_users.*, trips.*')
Overriding the select part of the SQL.
Not very pretty in my opinion thou, I shouldn't be messing with tables and queries but models, specially in such a common case of a m2m association with extra data in the middle.
You'll want to use joins rather than includes for this... See the following Rails Guide:
http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Essentially you'd do something like this:
current_user.trips.joins(:trips_users)
The includes method is used for eager loading, while joins actually performs the table join.
You could also try:
trips_users = current_user.trips_users.includes(:trip)
trips_users.first.pending?
trips_users.first.trip
Which should give you the trips_users records for that user but also eager loading the trips so that accessing them wouldn't hit the database again.

Rails Query Associations

I am fairly new to Rails and understand the basics of joins and associations but I am having a challenge with how to best use rails for some general queries. There is a lot of documentation I have reviewed but still not clear on best approach for this query. The models are as follows
User:
has_many :accounts
Account:
belongs_to :user
belongs_to :address, :class_name => "Location", :foreign_key => 'address_id'
Address:
belongs_to :account (This is really a many to one relationship where an address,
can belong to more than one account)
The query is to find all account addresses for the user. A standard sql query would look something like this.
SELECT Users.ID AS Users_ID, Users.Username, Account.acct_name, Address.*
FROM Address INNER JOIN (Users INNER JOIN Account ON Users.ID = Account.user_id)
ON Address.ID = Account.address_id
WHERE (((Users.ID)=2));
There seems to be a lot of power in rails to do this without a direct sql query. What is the best approach to doing this and are the models correct? Is the foreign key representation in the correct model.
Thank you in advance for your assistance.
You might be looking for a has_many :through association.
class User < AR::B
has_many :accounts
has_many :addresses, through: :accounts
end
You can then query all addresses of the user with #user.addresses.
If you want to take a look at the SQL query that ActiveRecord generates for your query, you may call .to_sql on a query: #user.addresses.to_sql.
Edit: Regarding the rest of your associations setup, you'll need to rework how Address and Account are joined. One approach might be this one.
class Account < AR::B
belongs_to :user
belongs_to :address # the accounts table should have an address_id integer column
end
class Address < AR::B
has_many :accounts
end
For more info on the options you can pass to the associations, see the Rails API Docs.
First, based on what you've described, the Address class should probably have the relationship has_many :accounts rather than belongs_to :account. The general rule of thumb is to use belongs_to when there's a foreign key in that model's table for the belongs_to table. But for this query that really doesn't matter, as you're starting from users.
Something like this should be close to what you want:
User.where(:id => 2).includes(:accounts => :addresses)
See the Rails guide for more information. The squeel gem is also useful for even more advanced querying capabilities.

Retrieve data from join table

I am new in RoR and I am trying to write a query on a join table that retrieve all the data I need
class User < ActiveRecord::Base
has_many :forms, :through => :user_forms
end
class Form < ActiveRecord::Base
has_many :users, :through => :user_forms
end
In my controller I can successfully retrieve all the forms of a user like this :
User.find(params[:u]).forms
Which gives me all the Form objects
But, I would like to add a new column in my join table (user_forms) that tells the status of the form (close, already filled, etc).
Is it possible to modify my query so that it can also retrieve columns from the user_forms table ?
it is possible. Once you've added the status column to user_forms, try the following
>> user = User.first
>> closed_forms = user.forms.where(user_forms: { status: 'closed' })
Take note that you don't need to add a joins because that's taken care of when you called user.forms.
UPDATE: to add an attribute from the user_forms table to the forms, try the following
>> closed_forms = user.forms.select('forms.*, user_forms.status as status')
>> closed_forms.first.status # should return the status of the form that is in the user_forms table
It is possible to do this using find_by_sql and literal sql. I do not know of a way to properly chain together rails query methods to create the same query, however.
But here's a modified example that I put together for a friend previously:
#user = User.find(params[:u])
#forms = #user.forms.find_by_sql("SELECT forms.*, user_forms.status as status FROM forms INNER JOIN user_forms ON forms.id = user_forms.form_id WHERE (user_forms.user_id = #{#user.id});")
And then you'll be able to do
#forms.first.status
and it'll act like status is just an attribute of the Form model.
First, I think you made a mistake.
When you have 2 models having has_many relations, you should set an has_and_belongs_to_many relation.
In most cases, 2 models are joined by
has_many - belongs_to
has_one - belongs_to
has_and_belongs_to_many - has_and_belongs_to_many
has_and_belongs_to_many is one of the solutions. But, if you choose it, you must create a join table named forms_users. Choose an has_and_belongs_to_many implies you can not set a status on the join table.
For it, you have to add a join table, with a form_id, a user_id and a status.
class User < ActiveRecord::Base
has_many :user_forms
has_many :forms, :through => :user_forms
end
class UserForm < ActiveRecord::Base
belongs_to :user
belongs_to :form
end
class Form < ActiveRecord::Base
has_many :user_forms
has_many :users, :through => :user_forms
end
Then, you can get
User.find(params[:u]).user_forms
Or
UserForm.find(:all,
:conditions => ["user_forms.user_id = ? AND user_forms.status = ?",
params[:u],
'close'
)
)
Given that status is really a property of Form, you probably want to add the status to the Forms table rather than the join table.
Then when you retrieve forms using your query, they will already have the status information retrieved with them i.e.
User.find(params[:u]).forms.each{ |form| puts form.status }
Additionally, if you wanted to find all the forms for a given user with a particular status, you can use queries like:
User.find(params[:u]).forms.where(status: 'closed')

Resources