Rails sort columns by column name in another table - ruby-on-rails

I'm using handles_sortable_columns to sort my columns in Rails 3 and was wondering if there is a way to sort by a column in another table other than using a join.
I have a column that is a lookup of the name of a column in another table.
That is, I have cities.country_id as a column and in the View template I do:
#cities.each do |city|
city.country.name
to display the name.
I would like to be able to sort by this column, but the countries.name, not the cities.country_id.
Does anyone know if this possible with the gem or any other simple way? If so how?
The only thing I can think of is to do an IF statement in the controller and catch the sorting by country and then run a different query using a join for that, but that's ugly and error-prone, was hoping for something more elegant.
Thanks.

Can you override to the to_s on the country model to return the name, then sort by that in the controller?
In your country model override to_s by:
def to_s
country.name
end
Then in your controller
#sorted_cities = #cities.sort_by { |obj| obj.country.to_s }

in addition to Webjedi answer..
#sorted_cities = #cities.includes(:country).sort_by { |obj| obj.country.to_s } #for eager loading

In the end I decided to use a join to get the right data to begin with. While Webjedi's answer is quite intriguing I didn't want to override any built-in functions, but it's good to know I can do that.
The code looks like this:
#countries = Country.select('countries.*, continents.name AS continent_name').joins{continent}.search(params[:term]).order(order).page(params[:page])

Related

Rails .where any field contains specific text

Is there a short-hand way of querying a Rails database for any record that has a field containing a specific piece of text? I know I could code every field with a .where("field_name LIKE ?", "my text"), but I have several fields and am wondering if there is a shorter way of doing this.
Thanks in advance.
I do not know of a framework-way to do so. You could code something using
my_attributes = YourModel.attributes
# delete attributes you do not need, like `id` etc.
# or just create an array with your desired attributes,
# whichever way is faster
queries = my_attributes.map { |attr| "#{attr} LIKE %insert_your_text_here%" }
# Do not use this if the text your looking for is provided by user input.
built_query = queries.join(" OR ")
YourModel.where(built_query)
This could bring you closer to your goal. Let me know if this makes sense to you.
edit: The answer https://stackoverflow.com/a/49458059/299781 mentions Ransack. That's a nice gem and takes the load off of you. Makes it easier, nicer and performs better :D
Glad you like this, but pay attention that you make your app open for sql injection, if you take user-input as the text you are looking for. (with this solution) Ransack would alleviate that.
class MyModel
scope :search_like, -> (field_name, search_string) {where("#{field_name} LIKE ?", "%#{search_string}%")}
end
then you can call it like:
MyModal.search_like('name', 'foobar')
UPDATE based on #holgar answer but beware if not indexed these searches can be slow on large data sets:
class MyModel
def self.multi_like(search_string)
my_attributes = [:first_name, :last_name] # probalby only use string fields here
queries = my_attributes.map { |attr| "#{attr} LIKE '%#{search_string}%'" }
where(queries.join(" OR "))
end
end
If you want full fledge text search based on params then you can use ransack gem

Rails custom model method in where query

In my rails app I have defined in the Kid model a calculation based on the fields from the Kids DB. the method is as follows:
def flip_date
self.dob.advance(months: 10)
end
I want to use this in my controller as I have a method where I am defining something as follows:
new_kids = Kid.where(discharge_date: nil).where('flip_date > ?', Date.current.advance(year: 1).beginning_of_year)
However I keep getting the following error:
SQLite3::SQLException: no such column: flip_date: SELECT "kids".* FROM "kids" WHERE "kids"."discharge_date" IS NULL AND (flip_date < '2017-01-01')
Any ideas on how can I make this work? All help is appreciated!
If you really want to use model methods take a look at http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select
For your case:
new_kids = Kid.where(discharge_date: nil).select{|k| k.flip_date > Date.current.advance(year: 1).beginning_of_year}
But select method takes every object in memory before returning final result. Hence I will advise to use normal where clause and instead of flip_date take dob (which is a column in database) in consideration.
Like this
new_kids = Kid.where(discharge_date: nil).where('dob > ?', <date criteria>)
The select method (http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/select) works great if you are okay with the return being an Array.
I am still looking for a way to do this with an ActiveRecord_Relation return.
If others know how to do this, it would be much appreciated if you can share.
This example doesn't respond to your specific code, but to the extent it helps someone else with a similar question, here's a very simple example of how .select can be really handy:
#expired_memberships = User.select{|u| u.membership_expired_yesterday?}
In that example you've looped through all your Users and filtered them based on a custom method you defined on the User model (membership_expired_yesterday?). Now you can easily do stuff with that collection like this example in a mailer:
#expirations.each do |user|
MembershipExpirationMailer.with(user: user).first_reminder.deliver_now
end

Have find_by in rails return multiple results

I am trying to return all groups created by a user. All of the groups are associated with the user id. When I run a find_by query it only returns the first result. Is there a way for it to return multiple?
Thanks in advance
I'm writing a separate answer because I can't comment on James Lowrey's answer as I don't have 50 points.
find_all_by is deprecated (Ruby 4.2).
To get a list of active records from models, do:
Model.where(attribute_name: val)
For example, to find all records in Vehicle table (having column name "model_name") such that the value of model_name is "Audi", do
#vehicles = Vehicle.where(model_name: "Audi")
You can iterate through these like so:
<% #vehicles.each do |vehicle| %>
Change find_by to find_all_by and it will return all matching results.
I think find_all_by may be deprecated now (at least, I couldn't get it to work). I believe the 'where' function is now recommended
where("name LIKE ?","%#{search}%")
where(instructor_id: params[:choosen_instructor_id])
where ("id = ?",id_val)
In case you didn't know, the ? and parameter list is to prevent SQL injections
User class has id as primary key.
which is stored as user_id in Group class
Then to find all groups associated with that user could be found as.
#groups=Group.find_all_by_user_id(user_id)

How to get table column value?

I write follow code to get one record from the table webeehs:
webeehs_result = Webeeh.find(:all, :conditions=>["webeeh_project_id=#{project_id}"])
Then I want to get one column value from this record, how could I do?
For example, the column name is webeeh_date.
first of all, never EVER write code like that. Building your own conditions as pure strings can leave you vulnerable to SQL injection exploits. If you must do conditions, then do it like this:
:conditions => ["webeeh_project_id = ?", project_id]
if you have a Project model, you should rename the webeeh_project_id column from your Webeeh model into project_id and have an association in your Project model like has_many :webeehs
Then, you won't need to call that find anymore, just do a p = Project.find(id) and then p.webeehs will return the webeehs you need.
the result will be an array which you can iterate through. And to get your webeeh.webeeh_date member, just call it like this:
result.each do |webeeh|
date = webeeh.webeeh_date
end
webeehs_result = Webeeh.findwebeeh_dates
is enough to get all columnn values.
For a different method and performance issues check the following: http://www.stopdropandrew.com/2010/01/28/finding-ids-fast-with-active-record.html
webeeh_result will usually be an array of results for the database.
You can iterate throughit using
webeehs_result.each do |webeeh|
# use "webeeh.webeeh_date" to access the column_name or do whatever you want with it.
end

rails/activerecord search eager loaded associations

I have a simple find statement as such:
m = MyModel.find(1, :include => :my_children)
With m.mychildren being an Array; is there anyway to find a particular record from within the array without having to iterate over the entire thing. If I do mychildren.find(1), a new DB query is issues, which doesn't make sense, since they are all loaded already
It looks like there's a little Rails magic going on here. Where Enumerable#find is being overridden by ActiveRecord::Base#find on methods created for associations.
On the upside Enumerable#find is aliased to Enumerable#detect.
Unfortunately Enumerable#find/Enumerable#detect have significantly different syntax from ActiveRecord::Base#find.
So you can't just do mychildren.find(1), instead you've got to do mychildren.detect{|c| c.id == 1} if you want to avoid hitting the database again. You may also want to consider extending Array for a more DRY way of doing this.
class Array
def id_find id
self.detect{|element| element.id == id}
end
end
I'm not quite sure what your asking, but have you tried select:
m.mychildren.select{ |child| child == <<some_statement>> }
This won't hit the database assuming you've used the :include option as you stated in your question.
Alternatively, if you know the number of the child you want, you should be able to just use
m.mychildren[1]

Resources