rails 3 order by another models column - ruby-on-rails

I have 2 models in my Rails 3 app which I use to describe people and where they live
Unfortunately I set these up without using associations
The 2 tables are setup like this
People
id
name
location_id
Locations
id
name
what I want to do is list all entries in the Peoples table ordered by Locations.name alphabetically and People.name alphabetically
I can do a simple sort using this code which groups each person by a location but I need to drill into the Locations table as well
#people = People.all(:order => '"location_id" ASC, "name" ASC')
Anyone have any idea?
Also is it a good idea to set up an association in the People class to say location_id is Locations.id

Add
belongs_to :location
To the People class
Then you can query the following way:
#people = People.joins(:location).order("locations.name ASC, people.name ASC")

Related

Rails: How to sort many-to-many relation

I have a many-to-many relationship between a model User and Picture. These are linked by a join table called Picturization.
If I obtain a list of users of a single picture, i.e. picture.users -> how can I ensure that the result obtained is sorted by either creation of the Picturization row (i.e. the order at which a picture was associated to a user). How would this change if I wanted to obtain this in order of modification?
Thanks!
Edit
Maybe something like
picture.users.where(:order => "created_at")
but this created_at refers to the created_at in picturization
Have an additional column something like sequence in picturization table and define sort order as default scope in your Picturization
default_scope :order => 'sequence ASC'
If you want default sort order based on modified_at then use following default scope
default_scope :order => 'modified_at DESC'
You can specify the table name in the order method/clause:
picture.users.order("picturizations.created_at DESC")
Well, in my case, I need to sort many-to-many relation by a column named weight in the middle-table. After hours of trying, I figured out two solutions to sort many-to-many relation.
Solution1: In Rails Way
picture.users.where(:order => "created_at")
cannot return a ActiveRecord::Relation sorted by Picturization's created_at column.
I have tried to rewrite a default_scope method in Picturization, but it does not work:
def self.default_scope
return Picturization.all.order(weight: :desc)
end
Instead, first, you need to get the ids of sorted Picturization:
ids = Picturization.where(user_id:user.id).order(created_at: :desc).ids
Then, you can get the sorted objects by using MySQL field functin
picture.users.order("field(picturizations.id, #{ids.join(",")})")
which generates SQL looks like this:
SELECT `users`.*
FROM `pictures` INNER JOIN `picturizations`
ON `pictures`.`id` = `picturizations`.`picture_id`
WHERE `picturizations`.`user_id = 1#for instance
ORDER BY field(picturizations.id, 9,18,6,8,7)#for instance
Solution2: In raw SQL Way
you can get the answer directly by using an order by function:
SELECT `users`.*
FROM `pictures` INNER JOIN `picturizations`
ON `pictures`.`id` = `picturizations`.`picture_id`
WHERE `picturizations`.`user_id = 1
order by picturizations.created_at desc

How do I select only the associated objects in a Rails "where" query?

I have a model Category, which has_many Products, and a Product in turn has_many Categories. When a user searches for a Category, I'd like to return the products of the matching Categories without losing my Arel object. Here's what I have so far:
Category.where("upper(title) like ?", search_term.upcase).map {|category| category.products}.flatten
This does the trick of returning the products, but of course what I have is an array and not Arel. I can get as far as adding an :includes(:products) clause, so I do indeed get the products back but I still have them attached to their categories. How do I adjust my query so that all I get back is an Arel that only addresses products?
If it is products that you want then you should probably start with the Product object when you are searching. For example ,you could do it like this:
Product.joins(:categories).where("upper(categories.title) like ?", search_term.upcase)
The reason I use joins instead of includes is that joins will perform an INNER JOIN instead of LEFT OUTER JOIN which is what you need to only return the products that are actually associated with the found categories.
To make it a little more elegant you could wrap it all up in a scope in your Product model like this:
# In Product.rb
scope :in_categories_like, Proc.new{ |search_term|
joins(:categories).where("upper(categories.title) like ?", search_term.upcase)
}
# In use
#products = Product.in_categories_like(params[:search_term])

Rails- Merge a find with 2 models

I want to build a rails request with 2 models.
I think it's quite simple, but I don't want to do a loop myself.
I'm in my country model:
def self.find_for_user(user_id)
wines = Wine.where("user_id = ?", user_id).group(:country_id)
where("countries.id IN ?", wines.map())
end
I want to get all countries depending the first request (the wines grouped by countries, I just need the countries)
I think I can do this in a single line where I put map() or another instruction. I just need to get all country_id fields for wines.
Thanks.
Assuming that you've got an association set up between wines and country (ie. has_many :wines in country.rb), I think this is what you're looking for:
def self.find_for_user(user_id)
joins(:wines).where('wines.user_id = ?', user_id).uniq
end
If all you want is all countries that have wine for a specific user, you can do that in SQL:
where("countries.id in (select country_id from wines where wines.user_id = ?)", user_id)

Rails basic search and PostgreSQL

I'm trying to create basic search in my web app. Here's code of search function.
def self.search(title, category_id, city_id)
if title || category_id || city_id
joins(:category).where('title LIKE (?) AND category.category_id IN (?) AND city.city_id IN (?)', "%#{title}%", "%#{category_id}%", "%#{city_id}%")
else
scoped
end
end
I have these associations in my model:
has_one :category
has_one :city
And I get this error
ActionView::Template::Error (PG::Error: ERROR: missing FROM-clause entry for ta
ble "category"
LINE 1: ..._id" = "events"."id" WHERE (title LIKE ('%%') AND category.c...
I'm using PostgreSQL. What I can do to remove this error?
The form of joins that you're using wants the association name, the SQL wants the table name. The table should be called categories.
A few other things:
I don't see you joining :city anywhere so your next error will be "Missing FROM-clause entry for table "city". The solution will be to .joins(:city) and use cities in the where. But keep reading anyway.
You don't need the parentheses around the value for LIKE, just title LIKE ? is fine.
You're using IN expressions for the category and city but you're giving them LIKE patterns and that won't work: the IDs will be numbers and you can use LIKE with numbers. If you're using IN then you'll usually want to supply a list of possible values, if you only want to match one value then just use = and a single value for the placeholder.
The categories table probably doesn't have a category_id column, similarly for the cities table and city_id column. Those two columns should be in your model's table.
Searching for a title when you don't have a title doesn't make much sense. Similarly for country and city.
That looks like a lot of problems but they can be fixed without too much effort:
def self.search(title, category_id, city_id)
rel = scoped
rel = rel.where('title like ?', "%#{title}%") if(title)
rel = rel.where('category_id = ?', category_id) if(category_id)
rel = rel.where('city_id = ?', city_id) if(city_id)
rel
end
and you don't even need joins or explicit table names at all.

Find all objects with broken association

I have two models in my rails app with a has many and belongs to association.
Category has many items and Item belongs to category.
These models are associated in the normal way through a category_id column in the Item model.
I'm looking for a quick way of finding all elements in the database with broken associations.
i.e. find all categories that exist with no associated items and items that exist with no associated category.
For example, if I have an item with a category_id of 7, but the category with id 7 has been deleted then this would be considered broken.
For your example, to find items with category_id's for categories which don't exist any more:
Item.where('NOT EXISTS (SELECT * FROM categories where category.id = item.category_id)')
You might want to look at this as well:
A rake task to track down missing database indexes (not foreign keys though, but indexes): https://github.com/eladmeidar/rails_indexes
A very effective way is using find_by_sql to let the database do the heavy lifting:
uncategorized_items = Item.find_by_sql("select * from items where category_id IS NULL")
Another way is a named scope:
class Item < ActiveRecord::Base
scope :uncategorized, where(:category_id => nil) # rails 3
# or...
named_scope :uncategorized, :conditions => 'category_id IS NULL'
end
These are just a couple of ideas. I assume that once you've found these broken associations you plan to fix them, right? You might want to use validates_associated in both models if it's important to you that this not happen again.
You can use find_by_sql and a left outer join to find all the items in one table but not another. Here, I use a downloads table and an image_files table (I've only included the SQL):
SELECT d.*, d.image_file_id
from downloads as d
LEFT OUTER JOIN image_files as i
ON i.id = d.image_file_id
WHERE d.image_file_id IS NULL

Resources